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::{
    array::Array,
    bundle::Bundle,
    clock::Clock,
    enum_::Enum,
    expr::Expr,
    int::{Bool, SInt, UInt},
    intern::{Intern, Interned},
    reset::{AsyncReset, Reset, SyncReset},
    source_location::SourceLocation,
};
use std::{fmt, hash::Hash, iter::FusedIterator, ops::Index};

#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub struct TypeProperties {
    pub is_passive: bool,
    pub is_storable: bool,
    pub is_castable_from_bits: bool,
    pub bit_width: usize,
}

#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub enum CanonicalType {
    UInt(UInt),
    SInt(SInt),
    Bool(Bool),
    Array(Array),
    Enum(Enum),
    Bundle(Bundle),
    AsyncReset(AsyncReset),
    SyncReset(SyncReset),
    Reset(Reset),
    Clock(Clock),
}

impl fmt::Debug for CanonicalType {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::UInt(v) => v.fmt(f),
            Self::SInt(v) => v.fmt(f),
            Self::Bool(v) => v.fmt(f),
            Self::Array(v) => v.fmt(f),
            Self::Enum(v) => v.fmt(f),
            Self::Bundle(v) => v.fmt(f),
            Self::AsyncReset(v) => v.fmt(f),
            Self::SyncReset(v) => v.fmt(f),
            Self::Reset(v) => v.fmt(f),
            Self::Clock(v) => v.fmt(f),
        }
    }
}

impl CanonicalType {
    pub fn type_properties(self) -> TypeProperties {
        match self {
            CanonicalType::UInt(v) => v.type_properties(),
            CanonicalType::SInt(v) => v.type_properties(),
            CanonicalType::Bool(v) => v.type_properties(),
            CanonicalType::Array(v) => v.type_properties(),
            CanonicalType::Enum(v) => v.type_properties(),
            CanonicalType::Bundle(v) => v.type_properties(),
            CanonicalType::AsyncReset(v) => v.type_properties(),
            CanonicalType::SyncReset(v) => v.type_properties(),
            CanonicalType::Reset(v) => v.type_properties(),
            CanonicalType::Clock(v) => v.type_properties(),
        }
    }
    pub fn is_passive(self) -> bool {
        self.type_properties().is_passive
    }
    pub fn is_storable(self) -> bool {
        self.type_properties().is_storable
    }
    pub fn is_castable_from_bits(self) -> bool {
        self.type_properties().is_castable_from_bits
    }
    pub fn bit_width(self) -> usize {
        self.type_properties().bit_width
    }
    pub fn can_connect(self, rhs: Self) -> bool {
        match self {
            CanonicalType::UInt(lhs) => {
                let CanonicalType::UInt(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::SInt(lhs) => {
                let CanonicalType::SInt(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Bool(lhs) => {
                let CanonicalType::Bool(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Array(lhs) => {
                let CanonicalType::Array(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Enum(lhs) => {
                let CanonicalType::Enum(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Bundle(lhs) => {
                let CanonicalType::Bundle(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::AsyncReset(lhs) => {
                let CanonicalType::AsyncReset(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::SyncReset(lhs) => {
                let CanonicalType::SyncReset(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Reset(lhs) => {
                let CanonicalType::Reset(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
            CanonicalType::Clock(lhs) => {
                let CanonicalType::Clock(rhs) = rhs else {
                    return false;
                };
                lhs.can_connect(rhs)
            }
        }
    }
}

pub trait MatchVariantAndInactiveScope: Sized {
    type MatchVariant: 'static + Send + Sync;
    type MatchActiveScope;
    fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope);
}

#[derive(Debug)]
pub struct MatchVariantWithoutScope<T: 'static + Send + Sync>(pub T);

impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWithoutScope<T> {
    type MatchVariant = T;
    type MatchActiveScope = ();

    fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope) {
        (self.0, ())
    }
}

pub trait FillInDefaultedGenerics {
    type Type: Type;
    fn fill_in_defaulted_generics(self) -> Self::Type;
}

impl<T: Type> FillInDefaultedGenerics for T {
    type Type = T;

    fn fill_in_defaulted_generics(self) -> Self::Type {
        self
    }
}

mod sealed {
    pub trait TypeOrDefaultSealed {}
    pub trait BaseTypeSealed {}
}

macro_rules! impl_base_type {
    ($name:ident) => {
        impl BaseType for $name {}
        impl sealed::BaseTypeSealed for $name {}
        impl From<$name> for CanonicalType {
            fn from(ty: $name) -> Self {
                Self::$name(ty)
            }
        }
    };
}

impl_base_type!(UInt);
impl_base_type!(SInt);
impl_base_type!(Bool);
impl_base_type!(Array);
impl_base_type!(Enum);
impl_base_type!(Bundle);
impl_base_type!(AsyncReset);
impl_base_type!(SyncReset);
impl_base_type!(Reset);
impl_base_type!(Clock);

impl sealed::BaseTypeSealed for CanonicalType {}

impl BaseType for CanonicalType {}

pub trait TypeOrDefault<D: Type>:
    sealed::TypeOrDefaultSealed + Copy + Eq + Hash + fmt::Debug
{
    type Type: Type;
    fn get<F: FnOnce() -> D>(self, default: F) -> Self::Type;
}

impl<T: Type> sealed::TypeOrDefaultSealed for T {}

impl<T: Type, D: Type> TypeOrDefault<D> for T {
    type Type = T;
    fn get<F: FnOnce() -> D>(self, _default: F) -> Self::Type {
        self
    }
}

impl sealed::TypeOrDefaultSealed for crate::__ {}

impl<D: Type> TypeOrDefault<D> for crate::__ {
    type Type = D;
    fn get<F: FnOnce() -> D>(self, default: F) -> Self::Type {
        default()
    }
}

pub trait Type:
    Copy + Hash + Eq + fmt::Debug + Send + Sync + 'static + FillInDefaultedGenerics<Type = Self>
{
    type BaseType: BaseType;
    type MaskType: Type<MaskType = Self::MaskType>;
    type MatchVariant: 'static + Send + Sync;
    type MatchActiveScope;
    type MatchVariantAndInactiveScope: MatchVariantAndInactiveScope<
        MatchVariant = Self::MatchVariant,
        MatchActiveScope = Self::MatchActiveScope,
    >;
    type MatchVariantsIter: Iterator<Item = Self::MatchVariantAndInactiveScope>
        + ExactSizeIterator
        + FusedIterator
        + DoubleEndedIterator;
    #[track_caller]
    fn match_variants(this: Expr<Self>, source_location: SourceLocation)
        -> Self::MatchVariantsIter;
    fn mask_type(&self) -> Self::MaskType;
    fn canonical(&self) -> CanonicalType;
    fn from_canonical(canonical_type: CanonicalType) -> Self;
    fn source_location() -> SourceLocation;
}

pub trait BaseType: Type<BaseType = Self> + sealed::BaseTypeSealed + Into<CanonicalType> {}

macro_rules! impl_match_variant_as_self {
    () => {
        type MatchVariant = crate::expr::Expr<Self>;
        type MatchActiveScope = ();
        type MatchVariantAndInactiveScope = crate::ty::MatchVariantWithoutScope<Self::MatchVariant>;
        type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
        fn match_variants(
            this: crate::expr::Expr<Self>,
            source_location: crate::source_location::SourceLocation,
        ) -> Self::MatchVariantsIter {
            let _ = source_location;
            std::iter::once(crate::ty::MatchVariantWithoutScope(this))
        }
    };
}

pub(crate) use impl_match_variant_as_self;

pub trait TypeWithDeref: Type {
    fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant;
}

impl Type for CanonicalType {
    type BaseType = CanonicalType;
    type MaskType = CanonicalType;
    impl_match_variant_as_self!();
    fn mask_type(&self) -> Self::MaskType {
        match self {
            CanonicalType::UInt(v) => v.mask_type().canonical(),
            CanonicalType::SInt(v) => v.mask_type().canonical(),
            CanonicalType::Bool(v) => v.mask_type().canonical(),
            CanonicalType::Array(v) => v.mask_type().canonical(),
            CanonicalType::Enum(v) => v.mask_type().canonical(),
            CanonicalType::Bundle(v) => v.mask_type().canonical(),
            CanonicalType::AsyncReset(v) => v.mask_type().canonical(),
            CanonicalType::SyncReset(v) => v.mask_type().canonical(),
            CanonicalType::Reset(v) => v.mask_type().canonical(),
            CanonicalType::Clock(v) => v.mask_type().canonical(),
        }
    }
    fn canonical(&self) -> CanonicalType {
        *self
    }
    fn from_canonical(canonical_type: CanonicalType) -> Self {
        canonical_type
    }
    fn source_location() -> SourceLocation {
        SourceLocation::builtin()
    }
}

pub trait StaticType: Type {
    const TYPE: Self;
    const MASK_TYPE: Self::MaskType;
    const TYPE_PROPERTIES: TypeProperties;
    const MASK_TYPE_PROPERTIES: TypeProperties;
}

pub type AsMask<T> = <T as Type>::MaskType;

pub struct AsMaskWithoutGenerics;

#[allow(non_upper_case_globals)]
pub const AsMask: AsMaskWithoutGenerics = AsMaskWithoutGenerics;

impl<T: Type> Index<T> for AsMaskWithoutGenerics {
    type Output = T::MaskType;

    fn index(&self, ty: T) -> &Self::Output {
        Interned::into_inner(Intern::intern_sized(ty.mask_type()))
    }
}