WIP: DynAccept trait, Visitor-based GC

DynAccept trait is necessary because we need some kind of Accept trait
that doesn't have generic methods - this is so we can dynamically
dispatch the .accept() method from a &dyn Obj type. I plan to work this
into the regular Accept trait and do away with the DynAccept distinction
entirely.

I'm also experimenting with using visitors to do GC, so that all
references are wrapped in a future GcRef, so they don't have to
implement that as part of their object. The GC will be able to visit any
Obj + ?Sized type, and can have a specific implementation if required.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-05-22 12:04:27 -04:00
parent 18f9810070
commit 7de005b160
10 changed files with 119 additions and 184 deletions

View File

@@ -21,23 +21,12 @@ impl Gc for BasicGc {
self.objects.push(owned);
}
fn mark(&mut self, alive: &[DynRef]) {
for obj in alive.iter() {
obj.borrow().mark();
}
// TODO : where should interned strings be marked?
fn mark(&mut self, _alive: &[DynRef]) {
todo!()
}
fn sweep(&mut self) {
// sweep
self.objects.retain(|cell| {
let obj = cell.borrow();
!obj.is_marked()
});
// unmark all objects
self.objects
.iter_mut()
.for_each(|cell| cell.borrow().unmark());
todo!()
}
}

View File

@@ -1,4 +1,4 @@
use crate::{mem::ptr::ObjCell, obj::prelude::*};
use crate::{mem::ptr::ObjCell, obj::prelude::*, visit::{Accept, Visit},};
pub trait Gc {
fn alloc<O: Obj + 'static>(&mut self, obj: O) -> ObjRef<O>;
@@ -9,3 +9,12 @@ pub trait Gc {
fn sweep(&mut self);
}
impl<O> Visit<O> for O
where O: Obj + Accept
{
type Out = ();
fn visit(&mut self, obj: &O) -> Self::Out {
obj.accept(self)
}
}

View File

@@ -1,4 +1,4 @@
use crate::obj::Obj;
use crate::{obj::Obj, visit::{DynAccept, Accept, Visit}};
use std::{
any,
cell::RefCell,
@@ -30,13 +30,6 @@ where
}
}
impl<O: Obj + ?Sized> AsRef<ObjCell<O>> for ObjRef<O> {
fn as_ref(&self) -> &ObjCell<O> {
// safe because pointer should point at live data assuming GC is working appropriately
unsafe { &*self.as_ptr() }
}
}
impl<O> ObjRef<O>
where
O: Obj,
@@ -47,6 +40,13 @@ where
}
}
impl<O: Obj + ?Sized> AsRef<ObjCell<O>> for ObjRef<O> {
fn as_ref(&self) -> &ObjCell<O> {
// safe because pointer should point at live data assuming GC is working appropriately
unsafe { &*self.as_ptr() }
}
}
impl<O> Deref for ObjRef<O>
where
O: Obj + ?Sized,
@@ -94,6 +94,14 @@ impl Debug for ObjRef<dyn Obj> {
}
}
impl<O> DynAccept for ObjRef<O>
where O: Obj + ?Sized + DynAccept
{
fn accept(&self, visitor: &mut impl Visit<Self, Out=()>) {
todo!()
}
}
//
// Comparison impls
//

View File

@@ -1,6 +1,7 @@
use crate::{
mem::ptr::{DynRef, ObjCell},
obj::prelude::*,
visit::Visit,
};
use std::{
cell::Cell,
@@ -60,48 +61,17 @@ impl Obj for Attrs {
self.this
}
fn is_marked(&self) -> bool {
self.marked.get()
}
fn mark(&self) {
if self.is_marked() {
return;
}
self.marked.set(true);
self.attrs.iter().for_each(|(_, v)| v.borrow().mark());
}
fn unmark(&self) {
if !self.is_marked() {
return;
}
self.marked.set(false);
self.attrs.iter().for_each(|(_, v)| v.borrow().unmark());
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
#[test]
fn test_attrs_new() {
use crate::mem::BasicGc;
//impl_accept!(Attrs);
let mut gc = BasicGc::default();
impl<G: Gc> Visit<Attrs> for G {
type Out = ();
let attrs_ref = Attrs::new(&mut gc, Default::default());
{
let mut attrs = attrs_ref.borrow_mut();
let sym = Sym::new(0);
attrs.insert(sym, attrs_ref.as_dyn());
assert!(ptr::eq(
dbg!(attrs.get(sym).unwrap().as_ptr()),
dbg!(attrs_ref.as_dyn().as_ptr())
// as_dyn() is important here - this will cause the test to fail otherwise
),);
fn visit(&mut self, _attrs: &Attrs) -> Self::Out {
todo!()
}
}

View File

@@ -1,4 +1,4 @@
use crate::obj::prelude::*;
use crate::{obj::prelude::*, visit::Visit};
use std::{any::Any, cell::Cell};
pub type DictRef = ObjRef<Dict>;
@@ -24,31 +24,20 @@ impl Obj for Dict {
self.attrs
}
fn is_marked(&self) -> bool {
self.marked.get()
}
fn mark(&self) {
if self.is_marked() {
return;
}
self.marked.set(true);
// TODO: dict
self.attrs.borrow().mark();
todo!()
}
fn unmark(&self) {
if !self.is_marked() {
return;
}
self.marked.set(false);
// TODO: dict
self.attrs.borrow().unmark();
todo!()
}
fn as_any(&self) -> &dyn Any {
self
}
}
//impl_accept!(Dict);
impl<G> Visit<Dict> for G
where G: Gc + Visit<Attrs>,
{
type Out = ();
fn visit(&mut self, dict: &Dict) -> Self::Out {
let attrs_ref: &Attrs = &dict.attrs.borrow();
self.visit(attrs_ref);
}
}

View File

@@ -24,15 +24,6 @@ impl Obj for NativeFun {
self.attrs
}
fn is_marked(&self) -> bool {
// native functions are always available
true
}
fn mark(&self) {}
fn unmark(&self) {}
fn as_any(&self) -> &dyn std::any::Any {
self
}
@@ -63,26 +54,6 @@ impl Obj for Fun {
self.attrs
}
fn is_marked(&self) -> bool {
self.marked.get()
}
fn mark(&self) {
if self.is_marked() {
return;
}
self.marked.set(true);
self.attrs.borrow().mark();
}
fn unmark(&self) {
if !self.is_marked() {
return;
}
self.marked.set(false);
self.attrs.borrow().unmark();
}
fn as_any(&self) -> &dyn Any {
self
}

View File

@@ -50,10 +50,5 @@ use std::any::Any;
pub trait Obj {
fn attrs(&self) -> ObjRef<Attrs>;
fn is_marked(&self) -> bool;
fn mark(&self);
fn unmark(&self);
fn as_any(&self) -> &dyn Any;
}

View File

@@ -1,4 +1,4 @@
use crate::obj::prelude::*;
use crate::{obj::prelude::*, visit::{DynAccept, Visit}};
use derivative::Derivative;
use std::{any::Any, cell::Cell};
@@ -35,27 +35,25 @@ impl Obj for Str {
self.attrs
}
fn is_marked(&self) -> bool {
self.marked.get()
}
fn mark(&self) {
if self.is_marked() {
return;
}
self.marked.set(true);
self.attrs.borrow().mark();
}
fn unmark(&self) {
if !self.is_marked() {
return;
}
self.marked.set(false);
todo!()
}
fn as_any(&self) -> &dyn Any {
self
}
}
//impl_accept!(Str);
impl<O> DynAccept for O
where O: Obj + ?Sized
{
fn accept(&self, visitor: &mut impl Visit<Self, Out=()>) {
visitor.visit(self)
}
}
impl<G: Gc> Visit<Str> for G {
type Out = ();
fn visit(&mut self, _s: &Str) -> Self::Out {
todo!()
}
}

View File

@@ -169,3 +169,39 @@ impl Spanned for BaseExpr {
self.span
}
}
////////////////////////////////////////////////////////////////////////////////
// Acceptor (visitor) impls
////////////////////////////////////////////////////////////////////////////////
impl_accept!(Stmt);
impl_default_accept!(Stmt : AssignStmt, Expr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Stmt::Assign(a) => visitor.visit(a),
Stmt::Expr(e) => visitor.visit(e),
}
}
);
impl_accept!(AssignStmt);
impl_accept!(Expr);
impl_default_accept!(Expr: BinExpr, UnExpr, FunCallExpr, IndexExpr, FunExpr, BaseExpr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Expr::Bin(b) => visitor.visit(b.as_ref()),
Expr::Un(u) => visitor.visit(u.as_ref()),
Expr::FunCall(f) => visitor.visit(f.as_ref()),
Expr::Index(i) => visitor.visit(i.as_ref()),
Expr::Fun(f) => visitor.visit(f.as_ref()),
Expr::Base(b) => visitor.visit(b),
}
}
);
impl_accept!(BinExpr);
impl_accept!(UnExpr);
impl_accept!(FunCallExpr);
impl_accept!(IndexExpr);
impl_accept!(FunExpr);
impl_accept!(BaseExpr);

View File

@@ -1,14 +1,14 @@
use crate::syn::ast::prelude::*;
pub trait Visit<A: Accept> {
pub trait Visit<A: ?Sized> {
type Out;
fn visit(&mut self, acceptor: &A) -> Self::Out;
}
pub trait Accept {
fn accept<V: Visit<Self>>(&self, visitor: &mut V) -> V::Out
where
Self: Sized;
fn accept<V: Visit<Self>>(&self, visitor: &mut V) -> V::Out;
}
pub trait DynAccept<Out=()> {
fn accept(&self, visitor: &mut impl Visit<Self, Out=Out>);
}
pub trait DefaultAccept<V: Visit<Self>>: Accept + Sized {
@@ -18,7 +18,7 @@ pub trait DefaultAccept<V: Visit<Self>>: Accept + Sized {
#[macro_export]
macro_rules! default_visitor {
($default:ident for $visitor:ty where Out = $out:ty) => {
impl crate::visit::Visit<$default> for $visitor {
impl $crate::visit::Visit<$default> for $visitor {
type Out = $out;
fn visit(&mut self, acceptor: &$default) -> Self::Out {
acceptor.default_accept(self)
@@ -30,61 +30,31 @@ macro_rules! default_visitor {
#[macro_export]
macro_rules! empty_visitor {
($default:ident for $visitor:ty) => {
impl crate::visit::Visit<$default> for $visitor {
impl $crate::visit::Visit<$default> for $visitor {
type Out = ();
fn visit(&mut self, _: &$default) -> Self::Out {}
}
};
}
#[macro_export]
macro_rules! impl_accept {
($what:ident) => {
impl crate::visit::Accept for $what {
fn accept<V: Visit<Self>>(&self, visitor: &mut V) -> V::Out {
($what:ty) => {
impl $crate::visit::Accept for $what {
fn accept<V: $crate::visit::Visit<Self>>(&self, visitor: &mut V) -> V::Out {
visitor.visit(self)
}
}
};
}
#[macro_export]
macro_rules! impl_default_accept {
($what:ident : $($requires:ident),+ => $($tail:tt)+) => {
impl<V, Out> DefaultAccept<V> for $what
where V: Visit<Self, Out=Out> $(+ Visit<$requires, Out=Out>)+
impl<V, Out> $crate::visit::DefaultAccept<V> for $what
where V: $crate::visit::Visit<Self, Out=Out> $(+ $crate::visit::Visit<$requires, Out=Out>)+
{
$($tail)+
}
};
}
impl_accept!(Stmt);
impl_default_accept!(Stmt : AssignStmt, Expr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Stmt::Assign(a) => visitor.visit(a),
Stmt::Expr(e) => visitor.visit(e),
}
}
);
impl_accept!(AssignStmt);
impl_accept!(Expr);
impl_default_accept!(Expr: BinExpr, UnExpr, FunCallExpr, IndexExpr, FunExpr, BaseExpr =>
fn default_accept(&self, visitor: &mut V) -> Out {
match self {
Expr::Bin(b) => visitor.visit(b.as_ref()),
Expr::Un(u) => visitor.visit(u.as_ref()),
Expr::FunCall(f) => visitor.visit(f.as_ref()),
Expr::Index(i) => visitor.visit(i.as_ref()),
Expr::Fun(f) => visitor.visit(f.as_ref()),
Expr::Base(b) => visitor.visit(b),
}
}
);
impl_accept!(BinExpr);
impl_accept!(UnExpr);
impl_accept!(FunCallExpr);
impl_accept!(IndexExpr);
impl_accept!(FunExpr);
impl_accept!(BaseExpr);