From 2392d5c2fa2c598ac0cf2bfa366eddf19517605c Mon Sep 17 00:00:00 2001 From: Ava Affine Date: Mon, 28 Jul 2025 23:02:06 +0000 Subject: [PATCH] Finish garbage collected heap This commit adds unit tests for garbage collection as well as a deep copy implementation for cons which fully deep copies the whole tree and a clone implementation for Datum which deep copies cons. Signed-off-by: Ava Affine --- hyphae/src/heap.rs | 113 +++++++++++++++++++++++++++++++++++++++++---- hyphae/src/vm.rs | 2 +- 2 files changed, 104 insertions(+), 11 deletions(-) diff --git a/hyphae/src/heap.rs b/hyphae/src/heap.rs index c616803..6e54603 100644 --- a/hyphae/src/heap.rs +++ b/hyphae/src/heap.rs @@ -48,15 +48,15 @@ use organelle::Number; #[repr(transparent)] pub struct Gc(NonNull>); -impl From> for Gc { - fn from(src: Rc) -> Self { +impl From> for Gc { + fn from(src: Rc) -> Self { Gc(NonNull::new(Box::into_raw(Box::new(src.clone()))) .expect("GC obj from rc nonnull ptr check")) } } -impl From for Gc { - fn from(value: Datum) -> Self { +impl From for Gc { + fn from(value: T) -> Self { Gc(NonNull::new(Box::into_raw(Box::new(Rc::from(value)))) .expect("GC obj from datum nonnull ptr check")) } @@ -103,7 +103,7 @@ macro_rules! shallow_copy_rc { } } -impl Clone for Gc { +impl Clone for Gc { fn clone(&self) -> Self { Gc(shallow_copy_rc!(self.0.as_ptr())) } @@ -116,7 +116,8 @@ impl Clone for Gc { impl Drop for Gc { fn drop(&mut self) { unsafe { - drop(Box::from_raw(self.0.as_ptr() as *mut Box>)) + let a = Box::>::from_raw(self.0.as_ptr()); + drop(a) } } } @@ -132,7 +133,7 @@ impl Gc { } } -#[derive(Clone, PartialEq)] +#[derive(PartialEq)] pub enum Datum { Number(Number), Bool(bool), @@ -145,14 +146,57 @@ pub enum Datum { None } +// implemented by hand to force deep copy on Cons datum +impl Clone for Datum { + fn clone(&self) -> Datum { + match self { + Datum::Number(n) => Datum::Number(n.clone()), + Datum::Bool(n) => Datum::Bool(n.clone()), + Datum::Cons(n) => Datum::Cons(n.deep_copy()), + Datum::Symbol(n) => Datum::Symbol(n.clone()), + Datum::Char(n) => Datum::Char(n.clone()), + Datum::String(n) => Datum::String(n.clone()), + Datum::Vector(n) => + Datum::Vector(n.clone()), + Datum::ByteVector(n) => + Datum::ByteVector(n.clone()), + Datum::None => Datum::None, + } + } + + fn clone_from(&mut self, source: &Self) { + *self = source.clone(); + } +} + #[derive(Clone, PartialEq)] pub struct Cons(pub Option>, pub Option>); impl Cons { pub fn deep_copy(&self) -> Cons { - // TODO: recursive deep copy through the whole list - Cons(self.0.as_ref().map(|x| x.deep_copy()), - self.1.as_ref().map(|x| x.deep_copy())) + macro_rules! car_cpy { + () => { + if let Some(ref car) = self.0 { + if let Datum::Cons(ref car) = **car { + Some(Datum::Cons(car.deep_copy()).into()) + } else { + self.0.as_ref().map(|x| x.deep_copy()) + } + } else { + None + } + } + } + + if let Some(ref next) = self.1 { + if let Datum::Cons(ref next) = **next { + Cons(car_cpy!(), Some(Datum::Cons(next.deep_copy()).into())) + } else { + Cons(car_cpy!(), self.1.as_ref().map(|x| x.deep_copy())) + } + } else { + Cons(car_cpy!(), None) + } } pub fn subsl(&self, start: isize, end: isize) -> Cons { @@ -242,3 +286,52 @@ impl Index for Cons { } } } + + +#[cfg(test)] +mod tests { + use super::*; + + struct GcTester<'a>(&'a mut bool); + + // clone implementation leaks memory... but its for test + impl Clone for GcTester<'_> { + fn clone(&self) -> Self { + unsafe { + GcTester(Box::into_raw(Box::new(self.0.clone())) + .as_mut() + .expect("Gc Test obj mut ref fail")) + } + } + + fn clone_from(&mut self, _: &Self) { + unimplemented!("test impl") + } + } + + // in non test code Id have to get the box back + impl Drop for GcTester<'_> { + fn drop(&mut self) { + *self.0 = true; + } + } + + #[test] + fn test_gc_basic_behavior() { + let mut flag = false; + let a = Into::>::into(GcTester(&mut flag)); + drop(a); + assert!(flag) + } + + #[test] + fn test_gc_shallow_copy() { + let mut flag = false; + let a = + Into::>::into(GcTester(&mut flag)).clone(); + drop(a); + assert!(flag) + } + + // TODO: test deep copy +} diff --git a/hyphae/src/vm.rs b/hyphae/src/vm.rs index 7bd245d..4fe21d3 100644 --- a/hyphae/src/vm.rs +++ b/hyphae/src/vm.rs @@ -102,7 +102,7 @@ impl VM { Address::Oper3 => &self.oper[2], Address::Oper4 => &self.oper[3], Address::Stack => &self.stack[$oper.1], - Address::Numer => e!("cannot access constant numeric"), + Address::Numer => e!("cannot access constant numeric data"), Address::Instr => e!("bad access to instruction data"), } };