fayalite 0.2.0

Hardware Description Language embedded in Rust, using FIRRTL's semantics
Documentation
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information

use crate::{
    expr::{ops::ArrayIndex, Expr, ToExpr},
    int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
    intern::{Intern, Interned, LazyInterned},
    module::transform::visit::{Fold, Folder, Visit, Visitor},
    source_location::SourceLocation,
    ty::{
        CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
    },
    util::ConstUsize,
};
use std::ops::Index;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
    element: LazyInterned<T>,
    len: Len::SizeType,
    type_properties: TypeProperties,
}

impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Array<{:?}, {}>", self.element, self.len())
    }
}

pub type Array<T = CanonicalType, const LEN: usize = DYN_SIZE> = ArrayType<T, ConstUsize<LEN>>;

#[allow(non_upper_case_globals)]
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
#[allow(non_upper_case_globals)]
pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;

impl<T: Type, Len: Size> ArrayType<T, Len> {
    const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties {
        let TypeProperties {
            is_passive,
            is_storable,
            is_castable_from_bits,
            bit_width,
        } = element;
        let Some(bit_width) = bit_width.checked_mul(len) else {
            panic!("array too big");
        };
        TypeProperties {
            is_passive,
            is_storable,
            is_castable_from_bits,
            bit_width,
        }
    }
    pub fn new(element: T, len: Len::SizeType) -> Self {
        let type_properties =
            Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len));
        Self {
            element: LazyInterned::Interned(element.intern_sized()),
            len,
            type_properties,
        }
    }
    pub fn element(&self) -> T {
        *self.element
    }
    pub fn len(self) -> usize {
        Len::as_usize(self.len)
    }
    pub fn is_empty(self) -> bool {
        self.len() == 0
    }
    pub fn type_properties(self) -> TypeProperties {
        self.type_properties
    }
    pub fn as_dyn_array(self) -> Array {
        Array::new_dyn(self.element().canonical(), self.len())
    }
    pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool {
        self.len() == rhs.len()
            && self
                .element()
                .canonical()
                .can_connect(rhs.element().canonical())
    }
}

impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> {
    pub fn new_static(element: T) -> Self {
        Self::new(element, Len::SizeType::default())
    }
}

impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
    const TYPE: Self = Self {
        element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
        len: Len::SIZE,
        type_properties: Self::TYPE_PROPERTIES,
    };
    const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
        element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
        len: Len::SIZE,
        type_properties: Self::MASK_TYPE_PROPERTIES,
    };
    const TYPE_PROPERTIES: TypeProperties =
        Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE);
    const MASK_TYPE_PROPERTIES: TypeProperties =
        Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE);
}

impl<T: Type> Array<T> {
    pub fn new_dyn(element: T, len: usize) -> Self {
        Self::new(element, len)
    }
}

impl<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> {
    fn fold(self, state: &mut State) -> Result<Self, State::Error> {
        state.fold_array_type(self)
    }

    fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> {
        Ok(ArrayType::new(self.element().fold(state)?, self.len))
    }
}

impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
    for ArrayType<T, Len>
{
    fn visit(&self, state: &mut State) -> Result<(), State::Error> {
        state.visit_array_type(self)
    }

    fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
        self.element().visit(state)
    }
}

impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
    type BaseType = Array;
    type MaskType = ArrayType<T::MaskType, Len>;
    type MatchVariant = Len::ArrayMatch<T>;
    type MatchActiveScope = ();
    type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
    type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;

    fn match_variants(
        this: Expr<Self>,
        source_location: SourceLocation,
    ) -> Self::MatchVariantsIter {
        let base = Expr::as_dyn_array(this);
        let base_ty = Expr::ty(base);
        let _ = source_location;
        let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
        std::iter::once(MatchVariantWithoutScope(
            Len::ArrayMatch::<T>::try_from(retval)
                .ok()
                .expect("unreachable"),
        ))
    }

    fn mask_type(&self) -> Self::MaskType {
        ArrayType::new(self.element().mask_type(), self.len)
    }

    fn canonical(&self) -> CanonicalType {
        CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
    }

    #[track_caller]
    fn from_canonical(canonical_type: CanonicalType) -> Self {
        let CanonicalType::Array(array) = canonical_type else {
            panic!("expected array");
        };
        Self::new(
            T::from_canonical(array.element()),
            Len::from_usize(array.len()),
        )
    }
    fn source_location() -> SourceLocation {
        SourceLocation::builtin()
    }
}

impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
    fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
        let base = Expr::as_dyn_array(*this);
        let base_ty = Expr::ty(base);
        let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
        Interned::into_inner(Intern::intern_sized(
            Len::ArrayMatch::<T>::try_from(retval)
                .ok()
                .expect("unreachable"),
        ))
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct ArrayWithoutGenerics;

impl<T: Type> Index<T> for ArrayWithoutGenerics {
    type Output = ArrayWithoutLen<T>;

    fn index(&self, element: T) -> &Self::Output {
        Interned::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayWithoutLen<T: Type> {
    element: T,
}

impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
    type Output = ArrayType<T, L::Size>;

    fn index(&self, len: L) -> &Self::Output {
        Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
    }
}