cell_gc/traits.rs
1//! The traits defined here are implementation details of cell_gc.
2//!
3//! Application code does not need to use these traits. They are public only so
4//! that `gc_heap_type!` can use it. Use the friendly macro!
5
6use gcref::GcRef;
7use ptr::Pointer;
8
9/// Trait for values that can be moved into a GC heap.
10///
11/// Cell-gc does not support GC allocation of arbitrary values: only values of
12/// types that implement `IntoHeap`. This trait is **not** meant to be
13/// implemented by hand: use the `gc_heap_type!` macro instead. All primitive
14/// types and many standard types support `IntoHeap`.
15///
16/// GC types come in pairs: an *in-heap* type, which is stored physically inside
17/// the heap, and may be packed with pointer fields and unsafe methods; and an
18/// `IntoHeap` type, which is safe, is used in application code, and lives on
19/// the stack. Both types have the same layout, bit for bit.
20///
21/// (The word "safe" above means that, as with everything generally in Rust, you
22/// can hack without fear. `IntoHeap` types don't expose raw Rust pointers or
23/// references to GC memory, they don't expose the unsafe in-heap types, and
24/// they obey Rust's safety and aliasing rules.)
25///
26/// Users never get a direct `&` reference to any in-heap value. All access is
27/// through safe `IntoHeap` types, like the `Ref` type that is automatically
28/// declared for you when you use `gc_heap_type!` to declare a struct.
29///
30/// * For primitive types, like `i32`, the two types are the same.
31///
32/// * Macro-generated "Ref" types are `IntoHeap` types; the corresponding
33/// in-heap types are effectively raw pointers.
34///
35/// * For macro-generated structs and enums, the user specifies the names of
36/// both types. Both types have identical fields or variants, but fields of
37/// the in-heap type are in-heap, and fields of the `IntoHeap` type are
38/// `IntoHeap`.
39///
40/// Implementation note: every type with `'static` lifetime should be safe to
41/// store in the heap, because ref types are never `'static`. Unfortunately I
42/// can't make rustc understand this. It would be *legal* to write this:
43///
44/// ```ignore
45/// unsafe impl<'h, T: Clone + 'static> IntoHeap<'h> for T { ...trivial... }
46/// ```
47///
48/// but alas, Rust thinks this impl conflicts with almost all others, making it
49/// impossible to get the macro to work. So instead we `impl IntoHeap for` a
50/// lot of individual types by hand.
51///
52/// **Liveness.** Some `IntoHeap` types ("Ref" types) are effectively smart
53/// pointers to in-heap values. To preserve safety, if an `IntoHeap` value is
54/// (or contains) a smart pointer to an in-heap value, then that value (and
55/// everything reachable from it) is protected from GC.
56///
57/// # Safety
58///
59/// In-heap objects are full of pointers; if `into_heap` puts garbage into
60/// them, GC will crash.
61///
62/// `trace` must be implemented with care in order to preserve the invariants
63/// of the GC graph-walking algorithm. Bugs there are very likely to lead to
64/// dangling pointers and hard-to-debug crashes down the road.
65///
66/// And this maybe goes without saying, but none of these methods may allocate
67/// or do anything else that could trigger garbage collection.
68///
69pub unsafe trait IntoHeap<'h>: Sized {
70 /// The type of the value when it is physically stored in the heap.
71 type In;
72
73 /// Convert the value to the form it should have in the heap.
74 /// This is for macro-generated code to call.
75 ///
76 /// This method must not be called while any direct Rust references to
77 /// heap objects exist. (However, user code never runs while such
78 /// references exist, so the method is not marked `unsafe`.)
79 fn into_heap(self) -> Self::In;
80
81 /// Extract the value from the heap. This turns any raw pointers in the
82 /// in-heap value into safe references, so while it's an unsafe function,
83 /// the result of a correct call can be safely handed out to user code.
84 ///
85 /// Unsafe to call: It is impossible for ordinary users to call this
86 /// safely, because `self` must be a direct, unwrapped reference to a value
87 /// stored in the GC heap, which ordinary users cannot obtain.
88 unsafe fn from_heap(&Self::In) -> Self;
89
90 /// # Safety
91 ///
92 /// It is impossible for ordinary users to call this safely, because `self`
93 /// must be a direct, unwrapped reference to a value stored in the GC heap,
94 /// which ordinary users cannot obtain.
95 unsafe fn trace<R>(&Self::In, tracer: &mut R)
96 where
97 R: Tracer;
98}
99
100/// Relate an `IntoHeap` type to the corresponding safe reference type.
101pub trait IntoHeapAllocation<'h>: IntoHeap<'h> {
102 type Ref: IntoHeap<'h>;
103
104 fn wrap_gcref(gcref: GcRef<'h, Self>) -> Self::Ref;
105}
106
107pub trait Tracer {
108 fn visit<'h, T>(&mut self, Pointer<T::In>)
109 where
110 T: IntoHeapAllocation<'h>;
111}
112
113// === Provided implmentations for primitive types
114
115macro_rules! gc_trivial_impl {
116 ($t:ty) => {
117 unsafe impl<'h> IntoHeap<'h> for $t {
118 type In = $t;
119 fn into_heap(self) -> $t { self }
120 unsafe fn from_heap(storage: &$t) -> $t { storage.clone() }
121 unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
122 }
123 }
124}
125
126gc_trivial_impl!(bool);
127gc_trivial_impl!(char);
128gc_trivial_impl!(i8);
129gc_trivial_impl!(u8);
130gc_trivial_impl!(i16);
131gc_trivial_impl!(u16);
132gc_trivial_impl!(i32);
133gc_trivial_impl!(u32);
134gc_trivial_impl!(i64);
135gc_trivial_impl!(u64);
136gc_trivial_impl!(isize);
137gc_trivial_impl!(usize);
138gc_trivial_impl!(f32);
139gc_trivial_impl!(f64);
140
141gc_trivial_impl!(String);
142
143macro_rules! gc_generic_trivial_impl {
144 (@as_item $it:item) => { $it };
145 ([$($x:tt)*] $t:ty) => {
146 gc_generic_trivial_impl! {
147 @as_item
148 unsafe impl<'h, $($x)*> IntoHeap<'h> for $t {
149 type In = $t;
150 fn into_heap(self) -> $t { self }
151 unsafe fn from_heap(storage: &$t) -> $t { (*storage).clone() }
152 unsafe fn trace<R>(_storage: &$t, _tracer: &mut R) where R: Tracer {}
153 }
154 }
155 }
156}
157
158gc_generic_trivial_impl!([T: ?Sized] &'static T);
159gc_generic_trivial_impl!([T: ?Sized] ::std::marker::PhantomData<T>);
160gc_generic_trivial_impl!([T: Clone + 'static] ::GCLeaf<T>);
161gc_generic_trivial_impl!([T: Clone + 'static] Box<T>);
162gc_generic_trivial_impl!([T: Clone + 'static] ::std::rc::Rc<T>);
163
164// Transitive implementations ("this particular kind of struct/enum is IntoHeap
165// if all its fields are") are slightly less trivial.
166
167unsafe impl<'h, T: IntoHeap<'h>> IntoHeap<'h> for Option<T> {
168 type In = Option<T::In>;
169
170 fn into_heap(self) -> Option<T::In> {
171 self.map(|t| t.into_heap())
172 }
173
174 unsafe fn trace<R>(storage: &Option<T::In>, tracer: &mut R)
175 where
176 R: Tracer,
177 {
178 match storage {
179 &None => (),
180 &Some(ref u) => T::trace(u, tracer),
181 }
182 }
183
184 unsafe fn from_heap(storage: &Option<T::In>) -> Option<T> {
185 match storage {
186 &None => None,
187 &Some(ref u) => Some(T::from_heap(u)),
188 }
189 }
190}
191
192macro_rules! gc_trivial_tuple_impl {
193 (@as_item $it:item) => { $it };
194 ($($t:ident),*) => {
195 gc_trivial_tuple_impl! {
196 @as_item
197 unsafe impl<'h, $($t: IntoHeap<'h>,)*> IntoHeap<'h> for ($($t,)*) {
198 type In = ($($t::In,)*);
199
200 #[allow(non_snake_case)] // because we use the type names as variable names (!)
201 fn into_heap(self) -> Self::In {
202 let ($($t,)*) = self;
203 ($($t.into_heap(),)*)
204 }
205
206 #[allow(non_snake_case)]
207 unsafe fn trace<R>(storage: &Self::In, tracer: &mut R)
208 where R: Tracer
209 {
210 let &($(ref $t,)*) = storage;
211
212 $(
213 <$t as $crate::traits::IntoHeap>::trace($t, tracer);
214 )*
215
216 // If the above `$(...)*` expansion is empty, we need this
217 // to quiet unused variable warnings for `tracer`.
218 let _ = tracer;
219 }
220
221 #[allow(non_snake_case)]
222 unsafe fn from_heap(storage: &Self::In) -> Self {
223 let &($(ref $t,)*) = storage;
224 ($( <$t as $crate::traits::IntoHeap>::from_heap($t), )*)
225 }
226 }
227 }
228 }
229}
230
231gc_trivial_tuple_impl!();
232gc_trivial_tuple_impl!(T);
233gc_trivial_tuple_impl!(T, U);
234gc_trivial_tuple_impl!(T, U, V);
235gc_trivial_tuple_impl!(T, U, V, W);
236gc_trivial_tuple_impl!(T, U, V, W, X);
237gc_trivial_tuple_impl!(T, U, V, W, X, Y);
238gc_trivial_tuple_impl!(T, U, V, W, X, Y, Z);