jrsonnet_gcmodule/
trace_impls.rs

1use std::borrow::{Borrow, BorrowMut};
2use std::ops::{Deref, DerefMut};
3
4use crate::trace::{Trace, Tracer};
5
6/// Mark types as acyclic. Opt-out the cycle collector.
7///
8/// See [`Trace::is_type_tracked`](trait.Trace.html#method.is_type_tracked) for details.
9/// In general, types including trait objects (directly or indirectly) should
10/// not be acyclic.
11///
12/// ## Examples
13///
14/// ```
15/// use jrsonnet_gcmodule::trace_acyclic;
16///
17/// struct X(u32);
18/// struct Y(String);
19/// struct Z<T>(fn (T));
20///
21/// trace_acyclic!(X);
22/// trace_acyclic!(Y);
23/// trace_acyclic!(<T> Z<T>);
24/// ```
25#[macro_export]
26macro_rules! trace_acyclic {
27    ( <$( $g:ident ),*> $( $t: tt )* ) => {
28        impl<$( $g: 'static ),*> $crate::Trace for $($t)* {
29            #[inline]
30            fn is_type_tracked() -> bool where Self: Sized { false }
31        }
32    };
33    ( $( $t: ty ),* ) => {
34        $( trace_acyclic!(<> $t); )*
35    };
36}
37
38/// Implement [`Trace`](trait.Trace.html) for simple container types.
39///
40/// ## Examples
41///
42/// ```
43/// use jrsonnet_gcmodule::Trace;
44/// use jrsonnet_gcmodule::trace_fields;
45///
46/// struct X<T1, T2> { a: T1, b: T2 };
47/// struct Y<T>(Box<T>);
48/// struct Z(Box<dyn Trace>);
49///
50/// trace_fields!(
51///     X<T1, T2> { a: T1, b: T2 }
52///     Y<T> { 0: T }
53///     Z { 0 }
54/// );
55/// ```
56#[macro_export]
57macro_rules! trace_fields {
58    ( $( $type:ty { $( $field:tt $(: $tp:ident )? ),* } )* ) => {
59        $(
60            impl< $( $( $tp: $crate::Trace )? ),* > $crate::Trace for $type {
61                fn trace(&self, tracer: &mut $crate::Tracer) {
62                    let _ = tracer;
63                    $( (&self . $field ).trace(tracer); )*
64                }
65                #[inline]
66                fn is_type_tracked() -> bool {
67                    $( $( if $tp::is_type_tracked() { return true } )? )*
68                    false
69                }
70            }
71        )*
72    };
73}
74
75trace_acyclic!(bool, char, f32, f64, i16, i32, i64, i8, isize, u16, u32, u64, u8, usize);
76trace_acyclic!(());
77trace_acyclic!(String, &'static str);
78
79mod tuples {
80    trace_fields!(
81        (A,) { 0: A }
82        (A, B,) { 0: A, 1: B }
83        (A, B, C,) { 0: A, 1: B, 2: C }
84        (A, B, C, D,) { 0: A, 1: B, 2: C, 3: D }
85        (A, B, C, D, E,) { 0: A, 1: B, 2: C, 3: D, 4: E }
86        (A, B, C, D, E, F,) { 0: A, 1: B, 2: C, 3: D, 4: E, 5: F }
87        (A, B, C, D, E, F, G,) { 0: A, 1: B, 2: C, 3: D, 4: E, 5: F, 6: G }
88    );
89}
90
91mod borrow {
92    use super::*;
93    use std::borrow::Cow;
94
95    impl<T: ToOwned + ?Sized> Trace for Cow<'static, T>
96    where
97        T::Owned: Trace,
98    {
99        fn trace(&self, tracer: &mut Tracer) {
100            if let Cow::Owned(v) = self {
101                v.trace(tracer)
102            }
103        }
104
105        #[inline]
106        fn is_type_tracked() -> bool {
107            T::Owned::is_type_tracked()
108        }
109    }
110}
111
112mod boxed {
113    use super::*;
114    impl<T: Trace> Trace for Box<T> {
115        fn trace(&self, tracer: &mut Tracer) {
116            self.as_ref().trace(tracer);
117        }
118
119        #[inline]
120        fn is_type_tracked() -> bool {
121            T::is_type_tracked()
122        }
123    }
124}
125
126mod cell {
127    use super::*;
128    use std::cell;
129
130    impl<T: Copy + Trace> Trace for cell::Cell<T> {
131        fn trace(&self, tracer: &mut Tracer) {
132            self.get().trace(tracer);
133        }
134
135        #[inline]
136        fn is_type_tracked() -> bool {
137            T::is_type_tracked()
138        }
139    }
140
141    impl<T: Trace> Trace for cell::RefCell<T> {
142        fn trace(&self, tracer: &mut Tracer) {
143            // If the RefCell is currently borrowed we
144            // assume there's an outstanding reference to this
145            // cycle so it's ok if we don't trace through it.
146            // If the borrow gets leaked somehow then we're going
147            // to leak the cycle.
148            if let Ok(x) = self.try_borrow() {
149                x.trace(tracer);
150            }
151        }
152
153        #[inline]
154        fn is_type_tracked() -> bool {
155            T::is_type_tracked()
156        }
157    }
158
159    impl<T: Trace> Trace for cell::OnceCell<T> {
160        fn trace(&self, tracer: &mut Tracer) {
161            if let Some(x) = self.get() {
162                x.trace(tracer)
163            }
164        }
165
166        #[inline]
167        fn is_type_tracked() -> bool {
168            T::is_type_tracked()
169        }
170    }
171}
172
173mod collections {
174    use super::*;
175    use std::collections;
176    use std::hash;
177
178    impl<K: Trace, V: Trace> Trace for collections::BTreeMap<K, V> {
179        fn trace(&self, tracer: &mut Tracer) {
180            for (k, v) in self {
181                k.trace(tracer);
182                v.trace(tracer);
183            }
184        }
185
186        #[inline]
187        fn is_type_tracked() -> bool {
188            K::is_type_tracked() || V::is_type_tracked()
189        }
190    }
191
192    impl<K: Eq + hash::Hash + Trace, V: Trace> Trace for collections::HashMap<K, V> {
193        fn trace(&self, tracer: &mut Tracer) {
194            for (k, v) in self {
195                k.trace(tracer);
196                v.trace(tracer);
197            }
198        }
199
200        #[inline]
201        fn is_type_tracked() -> bool {
202            K::is_type_tracked() || V::is_type_tracked()
203        }
204    }
205
206    impl<T: Trace> Trace for collections::LinkedList<T> {
207        fn trace(&self, tracer: &mut Tracer) {
208            for t in self {
209                t.trace(tracer);
210            }
211        }
212
213        #[inline]
214        fn is_type_tracked() -> bool {
215            T::is_type_tracked()
216        }
217    }
218
219    impl<T: Trace> Trace for collections::VecDeque<T> {
220        fn trace(&self, tracer: &mut Tracer) {
221            for t in self {
222                t.trace(tracer);
223            }
224        }
225
226        #[inline]
227        fn is_type_tracked() -> bool {
228            T::is_type_tracked()
229        }
230    }
231}
232
233mod vec {
234    use super::*;
235    impl<T: Trace> Trace for Vec<T> {
236        fn trace(&self, tracer: &mut Tracer) {
237            for t in self {
238                t.trace(tracer);
239            }
240        }
241
242        #[inline]
243        fn is_type_tracked() -> bool {
244            T::is_type_tracked()
245        }
246    }
247}
248
249// See https://github.com/rust-lang/rust/issues/56105#issuecomment-465709105
250#[allow(unknown_lints)]
251#[allow(coherence_leak_check)]
252mod func {
253    trace_acyclic!(<X> fn() -> X);
254
255    trace_acyclic!(<A, X> fn(&A) -> X);
256    trace_acyclic!(<A, X> fn(A) -> X);
257
258    trace_acyclic!(<A, B, X> fn(&A, &B) -> X);
259    trace_acyclic!(<A, B, X> fn(A, &B) -> X);
260    trace_acyclic!(<A, B, X> fn(&A, B) -> X);
261    trace_acyclic!(<A, B, X> fn(A, B) -> X);
262
263    trace_acyclic!(<A, B, C, X> fn(&A, &B, &C) -> X);
264    trace_acyclic!(<A, B, C, X> fn(A, &B, &C) -> X);
265    trace_acyclic!(<A, B, C, X> fn(&A, B, &C) -> X);
266    trace_acyclic!(<A, B, C, X> fn(A, B, &C) -> X);
267    trace_acyclic!(<A, B, C, X> fn(&A, &B, C) -> X);
268    trace_acyclic!(<A, B, C, X> fn(A, &B, C) -> X);
269    trace_acyclic!(<A, B, C, X> fn(&A, B, C) -> X);
270    trace_acyclic!(<A, B, C, X> fn(A, B, C) -> X);
271
272    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, &C, &D) -> X);
273    trace_acyclic!(<A, B, C, D, X> fn(A, &B, &C, &D) -> X);
274    trace_acyclic!(<A, B, C, D, X> fn(&A, B, &C, &D) -> X);
275    trace_acyclic!(<A, B, C, D, X> fn(A, B, &C, &D) -> X);
276    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, C, &D) -> X);
277    trace_acyclic!(<A, B, C, D, X> fn(A, &B, C, &D) -> X);
278    trace_acyclic!(<A, B, C, D, X> fn(&A, B, C, &D) -> X);
279    trace_acyclic!(<A, B, C, D, X> fn(A, B, C, &D) -> X);
280    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, &C, D) -> X);
281    trace_acyclic!(<A, B, C, D, X> fn(A, &B, &C, D) -> X);
282    trace_acyclic!(<A, B, C, D, X> fn(&A, B, &C, D) -> X);
283    trace_acyclic!(<A, B, C, D, X> fn(A, B, &C, D) -> X);
284    trace_acyclic!(<A, B, C, D, X> fn(&A, &B, C, D) -> X);
285    trace_acyclic!(<A, B, C, D, X> fn(A, &B, C, D) -> X);
286    trace_acyclic!(<A, B, C, D, X> fn(&A, B, C, D) -> X);
287    trace_acyclic!(<A, B, C, D, X> fn(A, B, C, D) -> X);
288
289    trace_acyclic!(<A, B, C, D, E, X> fn(A, B, C, D, E) -> X);
290    trace_acyclic!(<A, B, C, D, E, F, X> fn(A, B, C, D, E, F) -> X);
291}
292
293mod ffi {
294    use std::ffi;
295
296    trace_acyclic!(ffi::CString, ffi::NulError, ffi::OsString);
297}
298
299mod net {
300    use std::net;
301
302    trace_acyclic!(
303        net::AddrParseError,
304        net::Ipv4Addr,
305        net::Ipv6Addr,
306        net::SocketAddrV4,
307        net::SocketAddrV6,
308        net::TcpListener,
309        net::TcpStream,
310        net::UdpSocket
311    );
312}
313
314mod option {
315    use super::*;
316
317    impl<T: Trace> Trace for Option<T> {
318        fn trace(&self, tracer: &mut Tracer) {
319            if let Some(ref t) = *self {
320                t.trace(tracer);
321            }
322        }
323
324        fn is_type_tracked() -> bool {
325            T::is_type_tracked()
326        }
327    }
328}
329
330mod path {
331    use std::path;
332
333    trace_acyclic!(path::PathBuf);
334}
335
336mod process {
337    use std::process;
338
339    trace_acyclic!(
340        process::Child,
341        process::ChildStderr,
342        process::ChildStdin,
343        process::ChildStdout,
344        process::Command,
345        process::ExitStatus,
346        process::Output,
347        process::Stdio
348    );
349}
350
351mod rc {
352    use std::rc;
353
354    trace_acyclic!(<T> rc::Rc<T>);
355    trace_acyclic!(<T> rc::Weak<T>);
356}
357
358mod result {
359    use super::*;
360
361    impl<T: Trace, U: Trace> Trace for Result<T, U> {
362        fn trace(&self, tracer: &mut Tracer) {
363            match *self {
364                Ok(ref t) => t.trace(tracer),
365                Err(ref u) => u.trace(tracer),
366            }
367        }
368
369        fn is_type_tracked() -> bool {
370            T::is_type_tracked() || U::is_type_tracked()
371        }
372    }
373}
374
375mod sync {
376    use super::*;
377    use std::sync;
378
379    // See comment in Mutex for why this is acyclic.
380    trace_acyclic!(<T> sync::Arc<T>);
381
382    impl<T: Trace> Trace for sync::Mutex<T> {
383        fn trace(&self, tracer: &mut Tracer) {
384            // For single-thread collector (ObjectSpace):
385            // Locking is optional. See RefCell.
386            //
387            // For multi-thread collector (ThreadedObjectSpace):
388            // `ThreadedCcRef` is expected to be the only way to access a `T`
389            // stored in `ThreadedCc<T>`. `ThreadedCcRef` takes a lock so
390            // collector does not run. When the collector runs, `ThreadedCcRef`
391            // are dropped so locks are released.
392            // A special is when `T` is `Arc<Mutex<M>>`. It allows mutating `M`
393            // without going through `ThreadedCcRef`. This is handled by marking
394            // `Arc` as acyclic. The collector only cares about `trace`, and
395            // `trace` result for an `Arc` cannot be changed by another thread,
396            // even if `M` is mutable.
397            if let Ok(x) = self.try_lock() {
398                x.trace(tracer);
399            }
400        }
401
402        #[inline]
403        fn is_type_tracked() -> bool {
404            T::is_type_tracked()
405        }
406    }
407
408    impl<T: Trace> Trace for sync::RwLock<T> {
409        fn trace(&self, tracer: &mut Tracer) {
410            // See Mutex for why locking is optional.
411            //
412            // If read or write locks are already taken, that indicates
413            // outstanding references that keeps the objects alive.
414            if let Ok(x) = self.try_write() {
415                x.trace(tracer);
416            }
417        }
418
419        #[inline]
420        fn is_type_tracked() -> bool {
421            T::is_type_tracked()
422        }
423    }
424}
425
426mod thread {
427    use std::thread;
428
429    trace_acyclic!(<T> thread::JoinHandle<T>);
430    trace_acyclic!(<T> thread::LocalKey<T>);
431    trace_acyclic!(thread::Thread);
432}
433
434mod phantom {
435    use std::marker::PhantomData;
436
437    trace_acyclic!(<T> PhantomData<T>);
438}
439
440#[cfg(test)]
441mod tests {
442    use super::*;
443    use crate::Cc;
444    use std::cell::{Cell, RefCell};
445    use std::rc::Rc;
446
447    #[test]
448    fn test_is_type_tracked() {
449        assert!(!u8::is_type_tracked());
450        assert!(!<f32 as Trace>::is_type_tracked());
451        assert!(!String::is_type_tracked());
452        assert!(!Option::<u32>::is_type_tracked());
453        assert!(!Vec::<u8>::is_type_tracked());
454        assert!(!<(bool, f64)>::is_type_tracked());
455        assert!(!Cell::<u32>::is_type_tracked());
456        assert!(!RefCell::<String>::is_type_tracked());
457        assert!(TraceBox::<dyn Trace>::is_type_tracked());
458        assert!(RefCell::<TraceBox::<dyn Trace>>::is_type_tracked());
459        assert!(RefCell::<Vec::<TraceBox::<dyn Trace>>>::is_type_tracked());
460        assert!(Vec::<RefCell::<TraceBox::<dyn Trace>>>::is_type_tracked());
461        assert!(!Cc::<u8>::is_type_tracked());
462        assert!(!Vec::<Cc::<u8>>::is_type_tracked());
463
464        assert!(!<fn(u8) -> u8>::is_type_tracked());
465        assert!(!<fn(&u8) -> u8>::is_type_tracked());
466    }
467
468    #[test]
469    fn test_is_cyclic_type_tracked() {
470        type C1 = RefCell<Option<Rc<Box<S1>>>>;
471        struct S1(C1);
472        impl Trace for S1 {
473            fn trace(&self, t: &mut Tracer) {
474                self.0.trace(t);
475            }
476            fn is_type_tracked() -> bool {
477                // This is not an infinite loop because Rc is not tracked.
478                C1::is_type_tracked()
479            }
480        }
481
482        type C2 = RefCell<Option<Cc<Box<S2>>>>;
483        struct S2(C2);
484        impl Trace for S2 {
485            fn trace(&self, t: &mut Tracer) {
486                self.0.trace(t);
487            }
488            fn is_type_tracked() -> bool {
489                // C2::is_type_tracked() can cause an infinite loop.
490                true
491            }
492        }
493
494        assert!(!S1::is_type_tracked());
495        assert!(S2::is_type_tracked());
496    }
497}
498
499/// Box, that assumes inner type is tracked
500///
501/// Exists because plain `Box<T>` only accepts sized `T`
502#[derive(Debug, Clone)]
503pub struct TraceBox<T: ?Sized>(pub Box<T>);
504
505impl<T: ?Sized + Trace> Trace for TraceBox<T> {
506    fn trace(&self, tracer: &mut Tracer<'_>) {
507        self.0.trace(tracer);
508    }
509
510    fn is_type_tracked() -> bool {
511        true
512    }
513}
514
515impl<T: ?Sized> From<Box<T>> for TraceBox<T> {
516    fn from(inner: Box<T>) -> Self {
517        Self(inner)
518    }
519}
520
521impl<T: ?Sized> Deref for TraceBox<T> {
522    type Target = T;
523
524    fn deref(&self) -> &Self::Target {
525        &self.0
526    }
527}
528impl<T: Trace + ?Sized> DerefMut for TraceBox<T> {
529    fn deref_mut(&mut self) -> &mut Self::Target {
530        &mut self.0
531    }
532}
533
534impl<T: ?Sized> Borrow<T> for TraceBox<T> {
535    fn borrow(&self) -> &T {
536        &self.0
537    }
538}
539
540impl<T: ?Sized> BorrowMut<T> for TraceBox<T> {
541    fn borrow_mut(&mut self) -> &mut T {
542        &mut self.0
543    }
544}
545
546impl<T: ?Sized> AsRef<T> for TraceBox<T> {
547    fn as_ref(&self) -> &T {
548        &self.0
549    }
550}
551
552impl<T: ?Sized> AsMut<T> for TraceBox<T> {
553    fn as_mut(&mut self) -> &mut T {
554        &mut self.0
555    }
556}