Skip to content

Commit 772ffbd

Browse files
committed
[api] Fix doc tests
Now we have a 'dummy_impl' module that has a fake GC implementation. Maybe someday we should actually implement a real nop gc, like Java is adding. Right now the only doc-test we have is for `safepoint!` but at least that works :)
1 parent 4ef8388 commit 772ffbd

File tree

4 files changed

+174
-86
lines changed

4 files changed

+174
-86
lines changed

libs/derive/tests/basic.rs

Lines changed: 8 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use zerogc::{Gc, CollectorId, Trace, GcSafe, NullTrace};
1+
use zerogc::{Gc, CollectorId, Trace, GcSafe, NullTrace, dummy_impl};
22

33
use zerogc_derive::Trace;
44

@@ -41,84 +41,18 @@ struct NopTrace {
4141

4242
#[test]
4343
fn basic() {
44-
let _b = Basic::<dummy::DummyCollectorId> {
44+
let _b = Basic::<dummy_impl::DummyCollectorId> {
4545
value: String::new(),
4646
parent: None,
4747
children: vec![]
4848
};
49-
assert!(<Basic::<dummy::DummyCollectorId> as Trace>::NEEDS_TRACE);
50-
assert!(<BasicCopy::<dummy::DummyCollectorId> as Trace>::NEEDS_TRACE);
51-
assert!(<Basic::<dummy::DummyCollectorId> as GcSafe>::NEEDS_DROP);
52-
assert!(!<BasicCopy::<dummy::DummyCollectorId> as GcSafe>::NEEDS_DROP);
53-
assert_copy::<BasicCopy::<dummy::DummyCollectorId>>();
49+
assert!(<Basic::<dummy_impl::DummyCollectorId> as Trace>::NEEDS_TRACE);
50+
assert!(<BasicCopy::<dummy_impl::DummyCollectorId> as Trace>::NEEDS_TRACE);
51+
assert!(<Basic::<dummy_impl::DummyCollectorId> as GcSafe>::NEEDS_DROP);
52+
assert!(!<BasicCopy::<dummy_impl::DummyCollectorId> as GcSafe>::NEEDS_DROP);
53+
assert_copy::<BasicCopy::<dummy_impl::DummyCollectorId>>();
5454
assert_null_trace::<NopTrace>();
5555
assert!(!<NopTrace as Trace>::NEEDS_TRACE);
5656

57-
check_id::<dummy::DummyCollectorId>();
57+
check_id::<dummy_impl::DummyCollectorId>();
5858
}
59-
60-
mod dummy {
61-
use zerogc::{
62-
Gc, Trace, GcSafe, GcSystem, GcContext, CollectorId,
63-
NullTrace, TraceImmutable, GcVisitor
64-
};
65-
66-
pub struct DummyContext {}
67-
unsafe impl GcContext for DummyContext {
68-
type System = DummySystem;
69-
type Id = DummyCollectorId;
70-
71-
unsafe fn basic_safepoint<T: Trace>(&mut self, _value: &mut &mut T) {
72-
unimplemented!()
73-
}
74-
75-
unsafe fn freeze(&mut self) {
76-
unimplemented!()
77-
}
78-
79-
unsafe fn unfreeze(&mut self) {
80-
unimplemented!()
81-
}
82-
83-
unsafe fn recurse_context<T, F, R>(&self, _value: &mut &mut T, _func: F) -> R where T: Trace, F: for<'gc> FnOnce(&'gc mut Self, &'gc mut T) -> R {
84-
unimplemented!()
85-
}
86-
}
87-
88-
89-
pub struct DummySystem {}
90-
unsafe impl GcSystem for DummySystem {
91-
type Id = DummyCollectorId;
92-
type Context = DummyContext;
93-
}
94-
95-
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
96-
pub struct DummyCollectorId {}
97-
unsafe impl Trace for DummyCollectorId {
98-
const NEEDS_TRACE: bool = false;
99-
100-
fn visit<V: GcVisitor>(&mut self, _visitor: &mut V) -> Result<(), <V as GcVisitor>::Err> {
101-
Ok(())
102-
}
103-
}
104-
unsafe impl TraceImmutable for DummyCollectorId {
105-
fn visit_immutable<V: GcVisitor>(&self, _visitor: &mut V) -> Result<(), V::Err> {
106-
Ok(())
107-
}
108-
}
109-
110-
unsafe impl NullTrace for DummyCollectorId {}
111-
unsafe impl CollectorId for DummyCollectorId {
112-
type System = DummySystem;
113-
114-
unsafe fn gc_write_barrier<'gc, T, V>(
115-
_owner: &Gc<'gc, T, Self>,
116-
_value: &Gc<'gc, V, Self>,
117-
_field_offset: usize
118-
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc {}
119-
120-
unsafe fn assume_valid_system(&self) -> &Self::System {
121-
unimplemented!()
122-
}
123-
}
124-
}

src/dummy_impl.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
//! Dummy collector implementation for testing
2+
3+
use crate::{
4+
Trace, TraceImmutable, GcVisitor, NullTrace, CollectorId,
5+
GcSafe, GcSystem, GcContext,
6+
};
7+
use std::ptr::NonNull;
8+
9+
/// Fake a [Gc] that points to the specified value
10+
///
11+
/// This will never actually be collected
12+
/// and will always be valid
13+
pub fn gc<'gc, T: GcSafe + 'static>(ptr: &'static T) -> Gc<'gc, T> {
14+
unsafe {
15+
Gc::from_raw(
16+
DummyCollectorId { _priv: () },
17+
NonNull::from(ptr)
18+
)
19+
}
20+
}
21+
22+
/// Allocate a [(fake) Gc](Gc) that points to the specified
23+
/// value and leak it.
24+
///
25+
/// Since collection is unimplemented,
26+
/// this intentionally leaks memory.
27+
pub fn leaked<'gc, T: GcSafe + 'static>(value: T) -> Gc<'gc, T> {
28+
gc(Box::leak(Box::new(value)))
29+
}
30+
31+
/// An fake [garbage collected pointer](::zerogc::Gc)
32+
/// that uses the dummy collector system
33+
///
34+
/// This never actually collects any garbage
35+
pub type Gc<'gc, T> = crate::Gc<'gc, T, DummyCollectorId>;
36+
37+
/// A dummy implementation of [crate::GcSystem]
38+
/// which is useful for testing
39+
///
40+
/// This just blindly allocates memory and doesn't
41+
/// actually do any collection.
42+
pub struct DummyContext {
43+
_priv: ()
44+
}
45+
unsafe impl GcContext for DummyContext {
46+
type System = DummySystem;
47+
type Id = DummyCollectorId;
48+
49+
unsafe fn basic_safepoint<T: Trace>(&mut self, _value: &mut &mut T) {
50+
// safepoints are a nop since there is nothing to track
51+
}
52+
53+
unsafe fn freeze(&mut self) {
54+
unimplemented!()
55+
}
56+
57+
unsafe fn unfreeze(&mut self) {
58+
unimplemented!()
59+
}
60+
61+
unsafe fn recurse_context<T, F, R>(&self, value: &mut &mut T, func: F) -> R
62+
where T: Trace, F: for<'gc> FnOnce(&'gc mut Self, &'gc mut T) -> R {
63+
// safepoints are a nop since there is nothing to track
64+
let mut child = DummyContext { _priv: () };
65+
func(&mut child, &mut *value)
66+
}
67+
}
68+
69+
70+
/// A dummy implementation of [::zerogc::GcSystem]
71+
/// which is useful for testing
72+
///
73+
/// All methods panic and this should never be used
74+
/// in actual code.
75+
#[derive(Default)]
76+
pub struct DummySystem {
77+
_priv: ()
78+
}
79+
impl DummySystem {
80+
/// Create a new fake system for testing
81+
pub fn new() -> Self {
82+
DummySystem::default()
83+
}
84+
85+
/// Create a [DummyContext]
86+
///
87+
/// There are few restrictions on this
88+
/// because it doesn't actually do anything
89+
pub fn new_context(&self) -> DummyContext {
90+
DummyContext {
91+
_priv: ()
92+
}
93+
}
94+
}
95+
unsafe impl GcSystem for DummySystem {
96+
type Id = DummyCollectorId;
97+
type Context = DummyContext;
98+
}
99+
100+
/// The id for a [dummy gc pointer](Gc)
101+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
102+
pub struct DummyCollectorId {
103+
_priv: ()
104+
}
105+
unsafe impl Trace for DummyCollectorId {
106+
const NEEDS_TRACE: bool = false;
107+
108+
fn visit<V: GcVisitor>(&mut self, _visitor: &mut V) -> Result<(), <V as GcVisitor>::Err> {
109+
Ok(())
110+
}
111+
}
112+
unsafe impl TraceImmutable for DummyCollectorId {
113+
fn visit_immutable<V: GcVisitor>(&self, _visitor: &mut V) -> Result<(), V::Err> {
114+
Ok(())
115+
}
116+
}
117+
118+
unsafe impl NullTrace for DummyCollectorId {}
119+
unsafe impl CollectorId for DummyCollectorId {
120+
type System = DummySystem;
121+
122+
unsafe fn gc_write_barrier<'gc, T, V>(
123+
_owner: &Gc<'gc, T>,
124+
_value: &Gc<'gc, V>,
125+
_field_offset: usize
126+
) where T: GcSafe + ?Sized + 'gc, V: GcSafe + ?Sized + 'gc {}
127+
128+
unsafe fn assume_valid_system(&self) -> &Self::System {
129+
unimplemented!()
130+
}
131+
}

src/lib.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use core::fmt::{self, Debug, Formatter};
3434
mod manually_traced;
3535
pub mod cell;
3636
pub mod prelude;
37+
pub mod dummy_impl;
3738

3839
/// Invoke the closure with a temporary [GcContext],
3940
/// then perform a safepoint afterwards.
@@ -92,10 +93,11 @@ macro_rules! safepoint_recurse {
9293

9394
/// Create a new sub-context for the duration of the closure
9495
///
95-
/// The specified `root` object will be appended to the shadowstack
96-
/// and is guarenteed to live for the entire lifetime of the closure (and the created sub-context).
96+
/// The specified `root` object will be appended to the shadow-stack
97+
/// and is guarenteed to live for the entire lifetime of the closure
98+
/// (and the created sub-context).
9799
///
98-
/// Unlike `safepoint_recurse!` this doesn't imply a safepoint anywhere.
100+
/// Unlike [safepoint_recurse!] this doesn't imply a safepoint anywhere.
99101
///
100102
/// # Safety
101103
/// This doesn't actually mutate the original collector.
@@ -124,15 +126,21 @@ macro_rules! __recurse_context {
124126
/// Indicate it's safe to begin a garbage collection,
125127
/// while keeping the specified root alive.
126128
///
127-
/// All other garbage collected pointers that aren't reachable from the root are invalidated.
128-
/// They have a lifetime that references the [GcRef]
129+
/// All other garbage collected pointers that aren't reachable
130+
/// from the root are invalidated.
131+
/// They have a lifetime that references the [GcContext]
129132
/// and the borrow checker considers the safepoint a 'mutation'.
130133
///
131134
/// The root is exempted from the "mutation" and rebound to the new lifetime.
132135
///
133136
/// ## Example
134137
/// ```
135-
/// let root = safepoint!(collector, root);
138+
/// # use ::zerogc::safepoint;
139+
/// # let mut context = zerogc::dummy_impl::DummySystem::new().new_context();
140+
/// # // TODO: Can we please get support for non-Sized types like `String`?!?!?!
141+
/// let root = zerogc::dummy_impl::leaked(String::from("potato"));
142+
/// let root = safepoint!(context, root);
143+
/// assert_eq!(**root, "potato");
136144
/// ```
137145
///
138146
/// ## Safety

src/manually_traced/mod.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
/// However, using this macro is always better than a manual implementation, since it makes your intent clearer.
3131
///
3232
/// ## Usage
33-
/// ````
33+
/// ````no_test
3434
/// // You can use an arbitrary expression to acquire a lock's guard
3535
/// unsafe_trace_lock!(RefCell, target = T, |cell| cell.borrow());
3636
/// unsafe_trace_lock!(Mutex, target = T, |lock| lock.lock().unwrap());
@@ -97,7 +97,7 @@ macro_rules! unsafe_trace_lock {
9797
/// However, using this macro is always better than a manual implementation, since it makes your intent clearer.
9898
///
9999
/// ## Usage
100-
/// ````
100+
/// ````no_test
101101
/// // Easy to use for wrappers that `Deref` to their type parameter
102102
/// unsafe_trace_deref!(Box, target = T);
103103
/// unsafe_trace_deref!(Rc, target = T);
@@ -110,7 +110,18 @@ macro_rules! unsafe_trace_lock {
110110
/// */
111111
/// unsafe_trace_deref!(Cell, T; |cell| cell.get());
112112
/// unsafe_trace_deref!(Wrapping, T; |wrapping| &wrapping.0);
113-
/// unsafe_trace_deref!(NonZero, T; |nonzero| &nonzero.get());
113+
///
114+
/// // wrappers shouldn't need tracing if their innards don't
115+
/// assert!(!<Box<i32> as Trace>::NEEDS_TRACE);
116+
/// assert!(!<Cell<i32> as Trace>::NEEDS_TRACE);
117+
/// // Box needs to be dropped
118+
/// assert!(<Box<i32> as GcSafe>::NEEDS_DROP);
119+
/// // but Cell doesn't need to be dropped
120+
/// assert!(<Cell<i32> as GcSafe>::NEEDS_DROP);
121+
///
122+
/// // if the inside needs tracing, the outside does
123+
/// assert!(<Box<dummy_impl::Gc<'static, i32>> as Trace>::NEEDS_TRACE);
124+
/// assert!(<Cell<dummy_impl::Gc<'static, i32>> as Trace>::NEEDS_TRACE);
114125
/// ````
115126
///
116127
/// ## Safety
@@ -219,10 +230,14 @@ macro_rules! unsafe_trace_deref {
219230
/// In order to prevent ambiguity, this always requires the type of the element being traced.
220231
///
221232
/// ## Usage
222-
/// ````
233+
/// ````no_test
223234
/// unsafe_trace_iterable!(Vec, element = T);
224235
/// unsafe_trace_iterable!(HashMap, element = { (&K, &V) }; K, V);
225236
/// unsafe_trace_iterable!(HashSet, element = T);
237+
///
238+
/// assert!(!<Vec<i32> as Trace>::NEEDS_TRACE);
239+
/// assert!(<Vec<dummy_impl::Gc<'static, i32>> as Trace>::NEEDS_TRACE);
240+
/// assert!(<Vec<i32> as GcSafe>::NEEDS_DROP);
226241
/// ````
227242
///
228243
/// ## Safety
@@ -348,14 +363,14 @@ macro_rules! unsafe_gc_brand {
348363
};
349364
($target:ident, $($param:ident),+) => {
350365
unsafe impl<'new_gc, Id, $($param),*> $crate::GcBrand<'new_gc, Id> for $target<$($param),*>
351-
where Id: crate::CollectorId, $($param: $crate::GcBrand<'new_gc, Id>,)*
366+
where Id: $crate::CollectorId, $($param: $crate::GcBrand<'new_gc, Id>,)*
352367
$(<$param as $crate::GcBrand<'new_gc, Id>>::Branded: Trace,)* {
353368
type Branded = $target<$(<$param as $crate::GcBrand<'new_gc, Id>>::Branded),*>;
354369
}
355370
};
356371
($target:tt, immut = required; $($param:ident),+) => {
357372
unsafe impl<'new_gc, Id, $($param),*> $crate::GcBrand<'new_gc, Id> for $target<$($param),*>
358-
where Id: crate::CollectorId, $($param: $crate::GcBrand<'new_gc, Id> + TraceImmutable,)*
373+
where Id: $crate::CollectorId, $($param: $crate::GcBrand<'new_gc, Id> + TraceImmutable,)*
359374
$(<$param as $crate::GcBrand<'new_gc, Id>>::Branded: TraceImmutable,)* {
360375
type Branded = $target<$(<$param as $crate::GcBrand<'new_gc, Id>>::Branded),*>;
361376
}

0 commit comments

Comments
 (0)