pr47 0.1.3

A semi-experimental programming language. Still working in progress.
Documentation
use std::any::TypeId;
use std::marker::PhantomData;
use std::ptr::NonNull;

use xjbutil::void::Void;

use crate::data::Value;
use crate::data::generic::GenericTypeVT;
use crate::data::traits::{ChildrenType, StaticBase};
use crate::data::tyck::{TyckInfo, TyckInfoPool, ContainerTyckInfo};

pub enum UncheckedException {
    AlreadyAwaited { promise: Value },
    ArgCountMismatch { func_id: usize, expected: usize, got: usize },
    DivideByZero,
    InvalidBinaryOp { bin_op: char, lhs: Value, rhs: Value },
    InvalidCastOp { dest_type: &'static str, src: Value },
    InvalidUnaryOp { unary_op: char, src: Value },
    OwnershipCheckFailure { object: Value, expected_mask: u8 },
    UnexpectedNull { value: Value },
    IndexOutOfBound { indexed: Value, index: i64, len: usize }
}

pub type CheckedException = Value;

pub enum ExceptionInner {
    UncheckedException(UncheckedException),
    CheckedException(CheckedException)
}

#[derive(Clone, Copy)]
pub struct StackTrace {
    pub func_id: usize,
    pub insc_ptr: usize
}

impl StackTrace {
    pub fn new(func_id: usize, insc_ptr: usize) -> Self {
        Self { func_id, insc_ptr }
    }
}

pub struct Exception {
    inner: ExceptionInner,
    trace: Vec<StackTrace>
}

impl Exception {
    #[inline(never)] pub fn checked_exc(checked: CheckedException) -> Self {
        Self {
            inner: ExceptionInner::CheckedException(checked),
            trace: vec![]
        }
    }

    #[inline(never)] pub fn unchecked_exc(unchecked: UncheckedException) -> Self {
        Self {
            inner: ExceptionInner::UncheckedException(unchecked),
            trace: vec![]
        }
    }

    pub fn push_stack_trace(&mut self, func_id: usize, insc_ptr: usize) {
        self.trace.push(StackTrace::new(func_id, insc_ptr))
    }

    #[cfg(test)]
    pub fn assert_checked(&self) -> CheckedException {
        match &self.inner {
            ExceptionInner::CheckedException(e) => e.clone(),
            ExceptionInner::UncheckedException(_) => panic!()
        }
    }
}

impl StaticBase<Exception> for Void {
    fn type_name() -> String { "Exception".into() }

    fn children(vself: *const Exception) -> ChildrenType {
        match unsafe { &(*vself).inner } {
            ExceptionInner::UncheckedException(_) => None,
            ExceptionInner::CheckedException(checked) => {
                Some(Box::new(std::iter::once(unsafe { checked.ptr_repr })))
            }
        }
    }
}

#[repr(transparent)]
pub struct ExceptionContainer<E> {
    pub exception: Exception,
    _phantom: PhantomData<E>
}

impl<E> StaticBase<ExceptionContainer<E>> for Void
    where E: 'static,
          Void: StaticBase<E>
{
    fn type_id() -> TypeId {
        <Void as StaticBase<Exception>>::type_id()
    }

    fn tyck_info(tyck_info_pool: &mut TyckInfoPool) -> NonNull<TyckInfo> {
        let inner_tyck_info: NonNull<TyckInfo> =
            <Void as StaticBase<E>>::tyck_info(tyck_info_pool);
        tyck_info_pool.create_container_type(Self::type_id(), &[inner_tyck_info])
    }

    fn tyck(tyck_info: &TyckInfo) -> bool {
        if let TyckInfo::Container(ContainerTyckInfo { type_id, params }) = tyck_info {
            if Self::type_id() != *type_id {
                return false;
            }
            if unsafe { params.as_ref().len() } != 1 {
                return false;
            }
            let param0: NonNull<TyckInfo> = unsafe { *params.as_ref().get_unchecked(0) };
            <Void as StaticBase<E>>::tyck(unsafe { param0.as_ref() })
        } else {
            false
        }
    }

    fn type_name() -> String {
        format!("ExcContainer<{}>", <Void as StaticBase<E>>::type_name())
    }

    fn children(vself: *const ExceptionContainer<E>) -> ChildrenType {
        let r: &ExceptionContainer<E> = unsafe { &*vself };
        match r.exception.inner {
            ExceptionInner::UncheckedException(_) => None,
            ExceptionInner::CheckedException(checked) => {
                Some(Box::new(std::iter::once(unsafe { checked.ptr_repr })))
            }
        }
    }
}

pub fn create_exception_vt(
    tyck_info_pool: &mut TyckInfoPool,
    elem_type_name: &str,
    elem_tyck_info: NonNull<TyckInfo>
) -> GenericTypeVT {
    use crate::data::generic::gen_impls;

    #[cfg(debug_assertions)]
    unsafe fn move_out_ck(this: *mut (), out: *mut (), type_id: TypeId) {
        gen_impls::generic_move_out_ck::<Exception>(this, out, type_id)
    }

    #[cfg(not(debug_assertions))]
    unsafe fn move_out(this: *mut (), out: *mut ()) {
        gen_impls::generic_move_out::<Exception>(this, out)
    }

    unsafe fn children(this: *const ()) -> ChildrenType {
        gen_impls::generic_children::<Exception>(this)
    }

    unsafe fn exception_drop(this: *mut ()) {
        gen_impls::generic_drop::<Exception>(this)
    }

    let tyck_info: NonNull<TyckInfo> = tyck_info_pool.create_container_type(
        <Void as StaticBase<Exception>>::type_id(),
        &[elem_tyck_info]
    );
    let container_tyck_info: NonNull<ContainerTyckInfo> =
        unsafe { tyck_info.as_ref().get_container_tyck_info_unchecked() };

    GenericTypeVT {
        tyck_info: container_tyck_info,
        type_name: format!("ExcContainer<{}>", elem_type_name),
        #[cfg(debug_assertions)]
        move_out_fn: move_out_ck,
        #[cfg(not(debug_assertions))]
        move_out_fn: move_out,
        children_fn: children,
        drop_fn: exception_drop
    }
}