Rework DynAnyNode design to work with the borrow stack (#796)

This commit is contained in:
TrueDoctor 2022-10-15 03:02:58 +02:00 committed by Keavon Chambers
parent cef58b16c2
commit 562217015d
8 changed files with 198 additions and 80 deletions

View file

@ -25,12 +25,20 @@ pub fn downcast_ref<'a, V: StaticType>(i: &'a dyn DynAny<'a>) -> Option<&'a V> {
None
}
}
pub fn downcast<'a, V: StaticType>(i: Box<dyn DynAny<'a> + 'a>) -> Option<Box<V>> {
if i.type_id() == std::any::TypeId::of::<<V as StaticType>::Static>() {
let type_id = DynAny::type_id(i.as_ref());
if type_id == std::any::TypeId::of::<<V as StaticType>::Static>() {
// SAFETY: caller guarantees that T is the correct type
let ptr = Box::into_raw(i) as *mut dyn DynAny<'a> as *mut V;
Some(unsafe { Box::from_raw(ptr) })
} else {
// TODO: add log error
if type_id == std::any::TypeId::of::<&dyn DynAny<'static>>() {
panic!("downcast error: type_id == std::any::TypeId::of::<dyn DynAny<'a>>()");
}
println!("expected: {:?}", std::any::TypeId::of::<<V as StaticType>::Static>());
println!("actual one: {:?}", type_id);
None
}
}
@ -94,13 +102,29 @@ impl<'a> StaticType for &'a str {
impl StaticType for () {
type Static = ();
}
impl<'a, T: 'a + StaticType> StaticType for &'a T {
impl<'a, T: 'a + StaticType + ?Sized> StaticType for &'a T {
type Static = &'static <T as StaticType>::Static;
}
impl<T: StaticTypeSized, const N: usize> StaticType for [T; N] {
type Static = [<T as StaticTypeSized>::Static; N];
}
impl<'a> StaticType for dyn DynAny<'a> + '_ {
type Static = dyn DynAny<'static>;
}
pub trait IntoDynAny<'n>: Sized + StaticType + 'n {
fn into_dyn(self) -> Box<dyn DynAny<'n> + 'n> {
Box::new(self)
}
}
impl<'n, T: StaticType + 'n> IntoDynAny<'n> for T {}
impl From<()> for Box<dyn DynAny<'static>> {
fn from(_: ()) -> Box<dyn DynAny<'static>> {
Box::new(())
}
}
use core::{
cell::{Cell, RefCell, UnsafeCell},
iter::Empty,
@ -117,12 +141,21 @@ use std::{
impl_type!(Option<T>,Result<T, E>,Cell<T>,UnsafeCell<T>,RefCell<T>,MaybeUninit<T>,
Vec<T>, String, BTreeMap<K,V>,BTreeSet<V>, LinkedList<T>, VecDeque<T>,
BinaryHeap<T>, Box<T>, ManuallyDrop<T>, PhantomData<T>, PhantomPinned,Empty<T>,
BinaryHeap<T>, ManuallyDrop<T>, PhantomData<T>, PhantomPinned,Empty<T>,
Wrapping<T>, Duration, Once, Mutex<T>, RwLock<T>, bool, f32, f64, char,
u8, AtomicU8, u16,AtomicU16, u32,AtomicU32, u64,AtomicU64, usize,AtomicUsize,
i8,AtomicI8, i16,AtomicI16, i32,AtomicI32, i64,AtomicI64, isize,AtomicIsize,
i128, u128, AtomicBool, AtomicPtr<T>
);
impl<T: crate::StaticType + ?Sized> crate::StaticType for Box<T> {
type Static = Box<<T as crate::StaticType>::Static>;
}
fn test() {
let foo = (Box::new(&1 as &dyn DynAny<'static>), Box::new(&2 as &dyn DynAny<'static>));
//let bar = &foo as &dyn DynAny<'static>;
}
macro_rules! impl_tuple {
(@rec $t:ident) => { };
(@rec $_:ident $($t:ident)+) => {
@ -148,3 +181,9 @@ fn simple_downcast() {
let x = Box::new(3_u32) as Box<dyn DynAny>;
assert_eq!(*downcast::<u32>(x).unwrap(), 3_u32);
}
#[test]
#[should_panic]
fn simple_downcast_panic() {
let x = Box::new(3_i32) as Box<dyn DynAny>;
assert_eq!(*downcast::<u32>(x).expect("attempted to perform invalid downcast"), 3_u32);
}

View file

@ -8,11 +8,11 @@ use std::{
pub trait BorrowStack<'n> {
type Item;
/// # Safety
unsafe fn push(&'n self, value: Self::Item);
unsafe fn push(&self, value: Self::Item);
/// # Safety
unsafe fn pop(&'n self);
unsafe fn pop(&self);
/// # Safety
unsafe fn get(&'n self) -> &'n [Self::Item];
unsafe fn get(&self) -> &'n [Self::Item];
}
#[derive(Debug)]
@ -23,7 +23,7 @@ pub struct FixedSizeStack<'n, T> {
_phantom: PhantomData<&'n ()>,
}
impl<'n, T: Unpin> FixedSizeStack<'n, T> {
impl<'n, T: Unpin + 'n> FixedSizeStack<'n, T> {
pub fn new(capacity: usize) -> Self {
let layout = std::alloc::Layout::array::<MaybeUninit<T>>(capacity).unwrap();
let array = unsafe { std::alloc::alloc(layout) };
@ -44,12 +44,15 @@ impl<'n, T: Unpin> FixedSizeStack<'n, T> {
pub fn is_empty(&self) -> bool {
self.len.load(Ordering::SeqCst) == 0
}
pub fn push_fn(&self, f: impl FnOnce(&'n [T]) -> T) {
unsafe { self.push(f(self.get())) }
}
}
impl<'n, T> BorrowStack<'n> for FixedSizeStack<'n, T> {
type Item = T;
unsafe fn push(&'n self, value: Self::Item) {
unsafe fn push(&self, value: Self::Item) {
let len = self.len.load(Ordering::SeqCst);
assert!(len < self.capacity);
let ptr = self.data[len].as_ptr();
@ -57,13 +60,13 @@ impl<'n, T> BorrowStack<'n> for FixedSizeStack<'n, T> {
self.len.fetch_add(1, Ordering::SeqCst);
}
unsafe fn pop(&'n self) {
unsafe fn pop(&self) {
let ptr = self.data[self.len.load(Ordering::SeqCst)].as_ptr();
Box::from_raw(ptr as *mut T);
let _ = Box::from_raw(ptr as *mut T);
self.len.fetch_sub(1, Ordering::SeqCst);
}
unsafe fn get(&'n self) -> &'n [Self::Item] {
unsafe fn get(&self) -> &'n [Self::Item] {
std::slice::from_raw_parts(self.data.as_ptr() as *const T, self.len.load(Ordering::SeqCst))
}
}

View file

@ -48,6 +48,34 @@ where
}
}
pub trait AsBoxNode<'n, T>
where
&'n Self: Node<T>,
Self: 'n,
{
type Output;
fn eval_box(&'n self, input: T) -> <Self>::Output;
}
impl<'n, N: 'n, I> AsBoxNode<'n, I> for N
where
&'n N: Node<I, Output = N::Output>,
N: Node<I>,
Self: 'n,
{
type Output = <&'n N as Node<I>>::Output;
fn eval_box(&'n self, input: I) -> <Self>::Output {
self.eval(input)
}
}
impl<'n, T> Node<T> for &'n (dyn AsBoxNode<'n, T, Output = T> + 'n) {
type Output = T;
fn eval(self, input: T) -> Self::Output {
self.eval_box(input)
}
}
#[cfg(feature = "async")]
#[async_trait]
pub trait AsyncNode<T> {

View file

@ -240,7 +240,7 @@ mod test {
pub fn add_node() {
let a = ValueNode(42u32);
let b = ValueNode(6u32);
let cons_a = ConsNode(a);
let cons_a = ConsNode(a, PhantomData);
let sum = b.then(cons_a).then(AddNode);

View file

@ -115,49 +115,24 @@ pub trait ThenBox<Inter, Input> {
#[cfg(feature = "async")]
impl<'n, First: 'n, Inter, Input> ThenBox<Inter, Input> for alloc::boxed::Box<First> where &'n alloc::boxed::Box<First>: Node<Input, Output = Inter> {}
pub struct ConsNode<Root>(pub Root);
pub struct ConsNode<Root, T: From<()>>(pub Root, pub PhantomData<T>);
impl<Root, Input> Node<Input> for ConsNode<Root>
impl<Root, Input, T: From<()>> Node<Input> for ConsNode<Root, T>
where
Root: Node<()>,
Root: Node<T>,
{
type Output = (Input, <Root as Node<()>>::Output);
type Output = (Input, <Root as Node<T>>::Output);
fn eval(self, input: Input) -> Self::Output {
let arg = self.0.eval(());
let arg = self.0.eval(().into());
(input, arg)
}
}
impl<'n, Root: Node<()> + Copy, Input> Node<Input> for &'n ConsNode<Root> {
impl<'n, Root: Node<T> + Copy, T: From<()>, Input> Node<Input> for &'n ConsNode<Root, T> {
type Output = (Input, Root::Output);
fn eval(self, input: Input) -> Self::Output {
let arg = (&self.0).eval(());
let arg = self.0.eval(().into());
(input, arg)
}
}
pub struct ConsPassInputNode<Root>(pub Root);
impl<Root, L, R> Node<(L, R)> for ConsPassInputNode<Root>
where
Root: Node<R>,
{
type Output = (L, <Root as Node<R>>::Output);
fn eval(self, input: (L, R)) -> Self::Output {
let arg = self.0.eval(input.1);
(input.0, arg)
}
}
impl<'n, Root, L, R> Node<(L, R)> for &'n ConsPassInputNode<Root>
where
&'n Root: Node<R>,
{
type Output = (L, <&'n Root as Node<R>>::Output);
fn eval(self, input: (L, R)) -> Self::Output {
let arg = (&self.0).eval(input.1);
(input.0, arg)
}
}

View file

@ -1,39 +1,53 @@
#[cfg(test)]
mod tests {
use graphene_core::structural::*;
use std::marker::PhantomData;
use graphene_core::value::ValueNode;
use graphene_core::{structural::*, RefNode};
use borrow_stack::BorrowStack;
use graphene_std::any::{Any, DynAnyNode, DynAnyNodeTrait};
use dyn_any::{downcast, DynAny, IntoDynAny};
use graphene_std::any::{Any, DowncastNode, DynAnyNode, TypeErasedNode};
use graphene_std::ops::AddNode;
#[test]
fn borrow_stack() {
let stack = borrow_stack::FixedSizeStack::new(256);
unsafe {
let dynanynode: DynAnyNode<'_, _, ()> = DynAnyNode::new(&ValueNode(2_u32));
let refn = Box::new(dynanynode) as Box<dyn DynAnyNodeTrait>;
stack.push(refn);
}
unsafe {
let dynanynode: DynAnyNode<'_, _, &u32> = DynAnyNode::new(&ConsNode(ValueNode(2_u32)));
let refn = Box::new(dynanynode) as Box<dyn DynAnyNodeTrait>;
stack.push(refn);
}
unsafe {
let dynanynode: DynAnyNode<'_, _, (&u32, u32)> = DynAnyNode::new(&AddNode);
let refn = Box::new(dynanynode) as Box<dyn DynAnyNodeTrait>;
stack.push(refn);
let dynanynode: DynAnyNode<_, (), _, _> = DynAnyNode::new(ValueNode(2_u32));
stack.push(dynanynode.into_box());
}
stack.push_fn(|nodes| {
let downcast: DowncastNode<_, &u32> = DowncastNode::new(&nodes[0]);
let dynanynode: DynAnyNode<ConsNode<_, Any<'_>>, u32, _, _> = DynAnyNode::new(ConsNode(downcast, PhantomData));
dynanynode.into_box()
});
stack.push_fn(|_| {
let dynanynode: DynAnyNode<_, (u32, &u32), _, _> = DynAnyNode::new(AddNode);
dynanynode.into_box()
});
stack.push_fn(|nodes| {
let compose_node = nodes[1].after(&nodes[2]);
TypeErasedNode(Box::new(compose_node))
});
let mut input = Box::new(()) as Any;
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<(u32, &u32)>(result).unwrap(), (4_u32, &2_u32));
let result = unsafe { &stack.get()[1] }.eval_ref(4_u32.into_dyn());
let add = unsafe { &stack.get()[2] }.eval_ref(result);
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
let add = unsafe { &stack.get()[3] }.eval_ref(4_u32.into_dyn());
assert_eq!(*downcast::<u32>(add).unwrap(), 6_u32);
/*
for i in 0..3 {
println!("node_id: {}", i);
let value = unsafe { &stack.get()[i] };
input = value.eval_ref_dispatch(input);
}
input = value.eval_ref(input);
}*/
assert_eq!(*dyn_any::downcast::<u32>(input).unwrap(), 4)
//assert_eq!(*dyn_any::downcast::<u32>(result).unwrap(), 4)
//assert_eq!(4, *dyn_any::downcast::<u32>(DynamicAddNode.eval((Box::new(2_u32) as Dynamic, Box::new(2_u32) as Dynamic))).unwrap());
}

View file

@ -6,7 +6,7 @@ fn fmt_error<I>() -> String {
format!("DynAnyNode: input is not of correct type, expected {}", std::any::type_name::<I>())
}
pub struct DynAnyNode<'n, N: RefNode<I>, I>(pub N, pub PhantomData<&'n I>);
pub struct DynAnyNode<N, I: StaticType, O: StaticType, ORef: StaticType>(pub N, PhantomData<(I, O, ORef)>);
/*impl<'n, I: StaticType, N: RefNode<'n, &'n I, Output = O> + 'n, O: 'n + StaticType> Node<&'n dyn DynAny<'n>> for DynAnyNode<'n, N, I> {
type Output = Box<dyn dyn_any::DynAny<'n> + 'n>;
fn eval(self, input: &'n dyn DynAny<'n>) -> Self::Output {
@ -29,22 +29,42 @@ impl<'n, I: StaticType, N: RefNode<'n, I, Output = O> + 'n, O: 'n + StaticType>
Box::new(self.0.eval_ref(*input))
}
}*/
impl<'n, I: StaticType, N: RefNode<I, Output = O> + Copy + 'n, O: 'n + StaticType> Node<Any<'n>> for &'n DynAnyNode<'n, N, I> {
impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node<Any<'n>> for DynAnyNode<N, I, O, ORef>
where
N: Node<I, Output = O>,
{
type Output = Any<'n>;
fn eval(self, input: Any<'n>) -> Self::Output {
let input: Box<I> = dyn_any::downcast(input).unwrap_or_else(|| panic!("{}", fmt_error::<I>()));
Box::new(self.0.eval_ref(*input))
Box::new(self.0.eval(*input))
}
}
impl<'n, I: StaticType, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> Node<Any<'n>> for &'n DynAnyNode<N, I, O, ORef>
where
&'n N: Node<I, Output = ORef>,
{
type Output = Any<'n>;
fn eval(self, input: Any<'n>) -> Self::Output {
let input: Box<I> = dyn_any::downcast(input).unwrap_or_else(|| panic!("{}", fmt_error::<I>()));
Box::new((&self.0).eval_ref(*input))
}
}
pub struct TypeErasedNode<'n>(pub Box<dyn AsBoxNode<'n, Any<'n>, Output = Any<'n>>>);
impl<'n> Node<Any<'n>> for &'n TypeErasedNode<'n> {
type Output = Any<'n>;
fn eval(self, input: Any<'n>) -> Self::Output {
self.0.eval_box(input)
}
}
impl<'n, I: StaticType, N: RefNode<I, Output = O> + 'n + Copy, O: 'n + StaticType> DynAnyNode<'n, N, I>
impl<'n, I: StaticType + 'n, N: 'n, O: 'n + StaticType, ORef: 'n + StaticType> DynAnyNode<N, I, O, ORef>
where
N::Output: StaticType,
&'n N: Node<I, Output = ORef>,
{
pub fn new(n: N) -> Self {
DynAnyNode(n, PhantomData)
}
pub fn into_erased(&'n self) -> impl RefNode<Any<'n>, Output = Any<'n>> {
pub fn into_impl(&'n self) -> impl RefNode<Any<'n>, Output = Any<'n>> {
self
}
/*pub fn as_ref(&'n self) -> &'n AnyNode<'n> {
@ -53,22 +73,61 @@ where
pub fn into_ref_box(self) -> Box<dyn RefNode<Box<(dyn DynAny<'n> + 'n)>, Output = Box<(dyn DynAny<'n> + 'n)>> + 'n> {
Box::new(self)
}*/
pub fn into_ref(self: &'n &'n Self) -> &'n (dyn RefNode<Any<'n>, Output = Any<'n>> + 'n) {
pub fn as_ref(self: &'n &'n Self) -> &'n (dyn RefNode<Any<'n>, Output = Any<'n>> + 'n) {
self
}
pub fn into_box<'a: 'n>(self) -> TypeErasedNode<'n>
where
Self: 'a,
N: Node<I, Output = O>,
{
TypeErasedNode(Box::new(self))
}
}
pub struct DowncastNode<N, I: StaticType>(pub N, PhantomData<I>);
impl<N: Copy + Clone, I: StaticType> Clone for DowncastNode<N, I> {
fn clone(&self) -> Self {
Self(self.0, self.1)
}
}
impl<N: Copy + Clone, I: StaticType> Copy for DowncastNode<N, I> {}
impl<'n, N, O: 'n + StaticType> Node<Any<'n>> for DowncastNode<N, O>
where
N: Node<Any<'n>, Output = Any<'n>>,
{
type Output = O;
fn eval(self, input: Any<'n>) -> Self::Output {
let output = self.0.eval(input);
*dyn_any::downcast(output).unwrap_or_else(|| panic!("DowncastNode: {}", fmt_error::<O>()))
}
}
impl<'n, N, I: StaticType> DowncastNode<N, I>
where
N: Node<Any<'n>>,
{
pub fn new(n: N) -> Self {
DowncastNode(n, PhantomData)
}
}
/*
/// If we store a `Box<dyn RefNode>` in the stack then the origional DynAnyNode is dropped (because it is not stored by reference)
/// This trait is implemented directly by `DynAnyNode` so this means the borrow stack will hold by value
pub trait DynAnyNodeTrait<'n> {
fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n>;
}
impl<'n, I: StaticType, O: 'n + StaticType, Node: RefNode<I, Output = O> + Copy + 'n> DynAnyNodeTrait<'n> for DynAnyNode<'n, Node, I> {
impl<'n, I: StaticType, O: 'n + StaticType, Node: 'n> DynAnyNodeTrait<'n> for DynAnyNode<Node, I, O>
where
&'n Node: RefNode<I>,
{
fn eval_ref_dispatch(&'n self, input: Any<'n>) -> Any<'n> {
self.eval_ref(input)
}
}
}*/
use graphene_core::ops::Dynamic;
use graphene_core::{ops::Dynamic, AsBoxNode};
pub struct BoxedComposition<'a, Second> {
pub first: Box<dyn Node<(), Output = Dynamic<'a>>>,
pub second: Second,
@ -138,9 +197,9 @@ mod test {
#[test]
#[should_panic]
pub fn dyn_input_invalid_eval_panic() {
static ADD: &DynAnyNode<&AddNode, (u32, u32)> = &DynAnyNode(&AddNode, PhantomData);
static ADD: &DynAnyNode<AddNode, (u32, u32), u32, u32> = &DynAnyNode(AddNode, PhantomData);
let add = ADD.into_ref();
let add = ADD.as_ref();
add.eval_ref(Box::new(&("32", 32u32)));
}
/*#[test]
@ -161,11 +220,11 @@ mod test {
// If we put this until the push in a new scope then it failes to compile due to lifetime errors which I'm struggling to fix.
let value: &DynAnyNode<&ValueNode<(u32, u32)>, ()> = &DynAnyNode(&ValueNode((3u32, 4u32)), PhantomData);
let add: &DynAnyNode<&AddNode, &(u32, u32)> = &DynAnyNode(&AddNode, PhantomData);
let value: &DynAnyNode<ValueNode<(u32, u32)>, (), &(u32, u32), _> = &DynAnyNode(ValueNode((3u32, 4u32)), PhantomData);
let add: &DynAnyNode<AddNode, &(u32, u32), u32, _> = &DynAnyNode(AddNode, PhantomData);
let value_ref = (&value).into_ref();
let add_ref = (&add).into_ref();
let value_ref = value.as_ref();
let add_ref = add.as_ref();
vec.push(value_ref);
vec.push(add_ref);

View file

@ -111,7 +111,7 @@ impl<'a> IntoIterator for &'a Image {
pub fn file_node<'n, P: AsRef<Path> + 'n>() -> impl Node<P, Output = Result<Vec<u8>, Error>> {
let fs = ValueNode(StdFs).clone();
let fs = ConsNode(fs);
let fs = ConsNode(fs, PhantomData);
let file: ComposeNode<_, _, P> = fs.then(FileNode(PhantomData));
file.then(FlatMapResultNode::new(BufferNode))