WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 14s

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 3e76da494b
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
12 changed files with 974 additions and 7287 deletions

View file

@ -7,13 +7,14 @@ jobs:
- uses: https://code.forgejo.org/actions/checkout@v3
with:
fetch-depth: 0
- run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
source "$HOME/.cargo/env"
echo "$PATH" >> "$GITHUB_PATH"
- uses: https://github.com/Swatinem/rust-cache@v2
with:
save-if: ${{ github.ref == 'refs/heads/master' }}
- run: cargo test
- run: cargo test --features=unstable-doc
- run: cargo doc --features=unstable-doc
# FIXME: uncomment once the code works again
# - run: |
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
# source "$HOME/.cargo/env"
# echo "$PATH" >> "$GITHUB_PATH"
# - uses: https://github.com/Swatinem/rust-cache@v2
# with:
# save-if: ${{ github.ref == 'refs/heads/master' }}
# - run: cargo test
# - run: cargo test --features=unstable-doc
# - run: cargo doc --features=unstable-doc

View file

@ -1290,7 +1290,10 @@ impl Visitor {
memory,
paren,
ty_expr,
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())),
} => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
),
MemoryFn::MemoryArray {
memory_array,
paren,

View file

@ -1,671 +1,94 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
bundle::{BundleType, BundleValue},
expr::{
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
Expr, ToExpr,
},
intern::{Intern, Interned, InternedCompare, Memoize},
module::{
transform::visit::{Fold, Folder, Visit, Visitor},
ModuleBuilder, NormalModule,
},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
StaticValue, Type, TypeEnum, Value, ValueEnum,
},
util::{ConstBool, GenericConstBool, MakeMutSlice},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use std::{
any::Any,
borrow::{Borrow, BorrowMut},
fmt,
hash::Hash,
marker::PhantomData,
ops::IndexMut,
sync::Arc,
int::{KnownSize, UnknownSize, UNKNOWN_SIZE},
intern::{Intern, Interned},
ty::{CanonicalType, StaticType, Type},
util::GenericConstUsize,
};
use std::{marker::PhantomData, ops::Index};
mod sealed {
pub trait Sealed {}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Array<T: Type, Len: GenericConstUsize = UnknownSize> {
element: T,
len: usize,
_phantom: PhantomData<Len>,
}
pub trait ValueArrayOrSlice:
sealed::Sealed
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
+ Hash
+ fmt::Debug
+ Eq
+ Send
+ Sync
+ 'static
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
+ ToOwned
+ InternedCompare
{
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
type Match: 'static
+ Clone
+ Eq
+ fmt::Debug
+ Hash
+ Send
+ Sync
+ BorrowMut<[Expr<Self::Element>]>;
type MaskVA: ValueArrayOrSlice<
Element = <Self::ElementType as Type>::MaskValue,
ElementType = <Self::ElementType as Type>::MaskType,
LenType = Self::LenType,
MaskVA = Self::MaskVA,
> + ?Sized;
type IsStaticLen: GenericConstBool;
const FIXED_LEN_TYPE: Option<Self::LenType>;
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
fn len_from_len_type(v: Self::LenType) -> usize;
#[allow(clippy::result_unit_err)]
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
fn len_type(&self) -> Self::LenType;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn iter(&self) -> std::slice::Iter<Self::Element> {
Borrow::<[_]>::borrow(self).iter()
}
fn clone_to_arc(&self) -> Arc<Self>;
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
}
#[allow(non_upper_case_globals)]
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
impl<T> sealed::Sealed for [T] {}
impl<V: Value> ValueArrayOrSlice for [V]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = usize;
type Match = Box<[Expr<V>]>;
type MaskVA = [<Self::ElementType as Type>::MaskValue];
type IsStaticLen = ConstBool<false>;
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
(0..array.canonical_type().len())
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
.collect()
}
fn len_from_len_type(v: Self::LenType) -> usize {
v
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
Ok(v)
}
fn len_type(&self) -> Self::LenType {
self.len()
}
fn len(&self) -> usize {
<[_]>::len(self)
}
fn is_empty(&self) -> bool {
<[_]>::is_empty(self)
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::from(self)
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
MakeMutSlice::make_mut_slice(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
impl<T, const N: usize> sealed::Sealed for [T; N] {}
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = ();
type Match = [Expr<V>; N];
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
type IsStaticLen = ConstBool<true>;
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
std::array::from_fn(|index| {
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
})
}
fn len_from_len_type(_v: Self::LenType) -> usize {
N
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
if v == N {
Ok(())
} else {
Err(())
}
}
fn len_type(&self) -> Self::LenType {}
fn len(&self) -> usize {
N
}
fn is_empty(&self) -> bool {
N == 0
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::new(self.clone())
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
Arc::make_mut(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
element: VA::ElementType,
len: VA::LenType,
bit_width: usize,
}
pub trait ArrayTypeTrait:
Type<
CanonicalType = ArrayType<[DynCanonicalValue]>,
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
CanonicalValue = Array<[DynCanonicalValue]>,
MaskType = ArrayType<
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
>,
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ sealed::Sealed
+ Connect<Self>
{
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
+ ?Sized;
type Element: Value<Type = Self::ElementType>;
type ElementType: Type<Value = Self::Element>;
}
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
type ValueArrayOrSlice = VA;
type Element = VA::Element;
type ElementType = VA::ElementType;
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
fn clone(&self) -> Self {
Self {
element: self.element.clone(),
len: self.len,
bit_width: self.bit_width,
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
pub fn element(&self) -> &VA::ElementType {
impl<T: Type, Len: GenericConstUsize> Array<T, Len> {
pub const fn element(&self) -> &T {
&self.element
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.len)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn bit_width(&self) -> usize {
self.bit_width
}
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
ArrayType {
len: self.len(),
element: self.element,
bit_width: self.bit_width,
pub const fn len(&self) -> usize {
if Len::VALUE != UNKNOWN_SIZE {
debug_assert!(self.len == Len::VALUE);
Len::VALUE
} else {
self.len
}
}
#[track_caller]
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
Self::new_with_len_type(
}
impl<T: Type, Len: KnownSize> Array<T, Len> {
pub const fn new_static(element: T) -> Self {
Self {
element,
VA::try_len_type_from_len(len).expect("length should match"),
)
len: Len::VALUE,
_phantom: PhantomData,
}
}
#[track_caller]
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
panic!("array is too big: bit-width overflowed");
};
ArrayType {
}
impl<T: StaticType, Len: KnownSize> StaticType for Array<T, Len> {
fn static_type() -> Self {
Self::new_static(T::static_type())
}
}
impl<T: Type> Array<T> {
pub const fn new_dyn(element: T, len: usize) -> Self {
assert!(len != UNKNOWN_SIZE);
Self {
element,
len,
bit_width,
_phantom: PhantomData,
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
where
VA::ElementType: Fold<State>,
{
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::Error> {
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
impl<T: Type, Len: GenericConstUsize> Type for Array<T, Len> {
fn canonical(&self) -> CanonicalType {
CanonicalType::Array(Intern::intern_sized(Array::new_dyn(
self.element().canonical(),
self.len(),
)))
}
}
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
where
VA::ElementType: Visit<State>,
{
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)
#[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 }))
}
}
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
pub fn new_array(element: V::Type) -> Self {
ArrayType::new_with_len_type(element, ())
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct ArrayWithoutLen<T: Type> {
element: T,
}
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
fn static_type() -> Self {
Self::new_array(StaticType::static_type())
}
}
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> {
pub fn new_slice(element: V::Type, len: usize) -> Self {
ArrayType::new_with_len_type(element, len)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
type CanonicalType = ArrayType<[DynCanonicalValue]>;
type Value = Array<VA>;
type CanonicalValue = Array<[DynCanonicalValue]>;
type MaskType = ArrayType<VA::MaskVA>;
type MaskValue = Array<VA::MaskVA>;
type MatchVariant = VA::Match;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
}
fn mask_type(&self) -> Self::MaskType {
#[derive(Clone, Hash, Eq, PartialEq)]
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
type Input = ArrayType<T::ValueArrayOrSlice>;
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
fn inner(self, input: &Self::Input) -> Self::Output {
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
}
}
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
}
fn canonical(&self) -> Self::CanonicalType {
ArrayType {
element: self.element.canonical_dyn(),
len: self.len(),
bit_width: self.bit_width,
}
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::ArrayType(self.canonical())
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
Self {
element: VA::ElementType::from_dyn_canonical_type(t.element),
len: VA::try_len_type_from_len(t.len).expect("length should match"),
bit_width: t.bit_width,
}
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
this,
)?)
}
}
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
for ArrayType<Lhs>
{
}
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
element_ty: VA::ElementType,
value: Arc<VA>,
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
fn clone(&self) -> Self {
Self {
element_ty: self.element_ty.clone(),
value: self.value.clone(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
type Type = ArrayType<VA>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
Array {
element_ty: self.element_ty.canonical_dyn(),
value: AsRef::<[_]>::as_ref(&*self.value)
.iter()
.map(|v| v.to_canonical_dyn())
.collect(),
}
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
fn clone(&self) -> Self {
*self
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
type Input = Array<VA>;
type InputOwned = Array<VA>;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let mut bits = BitVec::with_capacity(input.ty().bit_width());
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
bits.extend_from_bitslice(&element.to_bits());
}
Intern::intern_owned(bits)
}
}
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
}
}
impl CanonicalValue for Array<[DynCanonicalValue]> {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Array(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
Value::to_bits_impl(this)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
pub fn element_ty(&self) -> &VA::ElementType {
&self.element_ty
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.value.len_type())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn value(&self) -> &Arc<VA> {
&self.value
}
pub fn set_element(&mut self, index: usize, element: VA::Element) {
assert_eq!(self.element_ty, element.ty());
VA::arc_make_mut(&mut self.value)[index] = element;
}
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
for element in value.iter() {
assert_eq!(element_ty, element.ty());
}
Self { element_ty, value }
}
pub fn into_slice(self) -> Array<[VA::Element]> {
Array {
element_ty: self.element_ty,
value: self.value.arc_to_arc_slice(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
where
VA::Element: StaticValue,
{
fn from(value: T) -> Self {
Self::new(StaticType::static_type(), value.into())
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
<[E]>::ty(self)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
<[E]>::to_expr(self)
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
type Type = ArrayType<[T::Value; N]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), ())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
#[derive(Clone, Debug)]
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
array: Arc<VA>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
type Item = VA::Element;
fn next(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next()?].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next_back()?].clone())
}
}
impl<VA: ValueArrayOrSlice> Array<VA> {
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
self.value.iter()
}
}
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
type Item = &'a VA::Element;
type IntoIter = std::slice::Iter<'a, VA::Element>;
fn into_iter(self) -> Self::IntoIter {
self.value.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
type Item = VA::Element;
type IntoIter = ArrayIntoIter<VA>;
fn into_iter(self) -> Self::IntoIter {
ArrayIntoIter {
indexes: 0..self.len(),
array: self.value,
}
}
}
#[derive(Clone, Debug)]
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
array: Expr<Array<VA>>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
type Item = Expr<VA::Element>;
fn next(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
pub fn len(self) -> usize {
self.canonical_type().len()
}
pub fn is_empty(self) -> bool {
self.canonical_type().is_empty()
}
pub fn iter(self) -> ArrayExprIter<VA> {
ArrayExprIter {
indexes: 0..self.len(),
array: self,
}
impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
type Output = Array<T>;
fn index(&self, len: usize) -> &Self::Output {
Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
}
}

View file

@ -1,806 +1,80 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
},
module::{ModuleBuilder, NormalModule},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantWithoutScope, StaticType, Type, TypeEnum,
TypeWithDeref, Value, ValueEnum,
},
};
use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap;
use std::{
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
sync::Arc,
intern::{Intern, Interned},
ty::{CanonicalType, StaticType, Type},
};
use hashbrown::HashSet;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct FieldType<T> {
pub struct BundleField {
pub name: Interned<str>,
pub flipped: bool,
pub ty: T,
pub ty: CanonicalType,
}
pub struct FmtDebugInStruct<'a, T> {
field: &'a FieldType<T>,
field_offset: usize,
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Bundle {
fields: Interned<[BundleField]>,
}
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
field:
&FieldType {
name,
flipped,
ref ty,
},
field_offset,
} = *self;
if flipped {
write!(f, "#[hdl(flip)] ")?;
impl Bundle {
#[track_caller]
pub fn new(fields: &[BundleField]) -> Self {
let fields = fields.intern();
let mut names = HashSet::new();
for field in fields {
assert!(
names.insert(field.name),
"duplicate field name: {:?}",
field.name
);
}
if f.alternate() {
writeln!(f, "/* offset = {field_offset} */")?;
}
write!(f, "{name}: ")?;
ty.fmt(f)
Self { fields }
}
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
impl Type for Bundle {
fn canonical(&self) -> CanonicalType {
CanonicalType::Bundle(*self)
}
}
impl<T> FieldType<T> {
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> FieldType<U> {
let Self { name, flipped, ty } = self;
FieldType {
name,
flipped,
ty: f(ty),
}
}
pub fn as_ref_ty(&self) -> FieldType<&T> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: &self.ty,
}
}
pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> {
FmtDebugInStruct {
field: self,
field_offset,
}
}
}
impl<T: Type> FieldType<T> {
pub fn canonical(&self) -> FieldType<T::CanonicalType> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical(),
}
}
pub fn to_dyn(&self) -> FieldType<Interned<dyn DynType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.to_dyn(),
}
}
pub fn canonical_dyn(&self) -> FieldType<Interned<dyn DynCanonicalType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical_dyn(),
}
}
}
impl FieldType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper<T: Type>(
self,
expected_name: &str,
expected_flipped: bool,
) -> T {
assert_eq!(&*self.name, expected_name, "field name doesn't match");
assert_eq!(
self.flipped, expected_flipped,
"field {expected_name} orientation (flipped or not) doesn't match"
);
let ty = &*self.ty;
if let Ok(ty) = <dyn DynCanonicalType>::downcast(ty) {
return T::from_canonical_type(ty);
}
let type_name = std::any::type_name::<T::CanonicalType>();
panic!("field {expected_name} type doesn't match, expected: {type_name:?}, got: {ty:?}");
}
}
#[derive(Clone, Eq)]
struct DynBundleTypeImpl {
fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>,
field_offsets: Interned<[usize]>,
is_passive: bool,
is_storable: bool,
is_castable_from_bits: bool,
bit_width: usize,
}
impl fmt::Debug for DynBundleTypeImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DynBundleType ")?;
f.debug_set()
.entries(
self.fields
.iter()
.enumerate()
.map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
)
.finish()
}
}
impl PartialEq for DynBundleTypeImpl {
fn eq(&self, other: &Self) -> bool {
self.fields == other.fields
}
}
impl Hash for DynBundleTypeImpl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.fields.hash(state);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynBundleType(Interned<DynBundleTypeImpl>);
impl fmt::Debug for DynBundleType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl DynBundleType {
pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self {
let is_passive = fields
.iter()
.all(|field| !field.flipped && field.ty.is_passive());
let is_storable = fields
.iter()
.all(|field| !field.flipped && field.ty.is_storable());
let is_castable_from_bits = fields
.iter()
.all(|field| !field.flipped && field.ty.is_castable_from_bits());
let mut name_indexes = HashMap::with_capacity(fields.len());
let mut field_offsets = Vec::with_capacity(fields.len());
let mut bit_width = 0usize;
for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate field name {name:?}: at both index {old_index} and {index}");
}
field_offsets.push(bit_width);
bit_width = bit_width
.checked_add(ty.bit_width())
.unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed"));
}
Self(
DynBundleTypeImpl {
fields,
name_indexes,
field_offsets: Intern::intern_owned(field_offsets),
is_passive,
is_storable,
is_castable_from_bits,
bit_width,
}
.intern_sized(),
)
}
pub fn is_passive(self) -> bool {
self.0.is_passive
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
}
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes
}
pub fn field_by_name(
&self,
name: Interned<str>,
) -> Option<FieldType<Interned<dyn DynCanonicalType>>> {
Some(self.0.fields[*self.0.name_indexes.get(&name)?])
}
pub fn field_offsets(self) -> Interned<[usize]> {
self.0.field_offsets
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynBundle {
ty: DynBundleType,
fields: Arc<[DynCanonicalValue]>,
}
impl DynBundle {
pub fn new(ty: DynBundleType, fields: Arc<[DynCanonicalValue]>) -> Self {
assert_eq!(
ty.fields().len(),
fields.len(),
"field values don't match type"
);
for (field_ty, field) in ty.fields().iter().zip(fields.iter()) {
assert_eq!(field_ty.ty, field.ty(), "field value doesn't match type");
}
DynBundle { ty, fields }
}
pub fn fields(&self) -> &Arc<[DynCanonicalValue]> {
&self.fields
}
}
pub trait TypeHintTrait: Send + Sync + fmt::Debug + SupportsPtrEqWithTypeId {
fn matches(&self, ty: &dyn DynType) -> Result<(), String>;
}
impl InternedCompare for dyn TypeHintTrait {
type InternedCompareKey = PtrEqWithTypeId;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(this)
}
fn interned_compare_key_weak(this: &std::sync::Weak<Self>) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap())
}
}
pub struct TypeHint<T: Type>(PhantomData<fn(T)>);
impl<T: Type> TypeHint<T> {
pub fn intern_dyn() -> Interned<dyn TypeHintTrait> {
Interned::cast_unchecked(
Self(PhantomData).intern_sized(),
|v| -> &dyn TypeHintTrait { v },
)
}
}
impl<T: Type> fmt::Debug for TypeHint<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeHint<{}>", std::any::type_name::<T>())
}
}
impl<T: Type + Hash> Hash for TypeHint<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: Type> Eq for TypeHint<T> {}
impl<T: Type> PartialEq for TypeHint<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: Type> Clone for TypeHint<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Type> Copy for TypeHint<T> {}
impl<T: Type> TypeHintTrait for TypeHint<T> {
fn matches(&self, ty: &dyn DynType) -> Result<(), String> {
match ty.downcast::<T>() {
Ok(_) => Ok(()),
Err(_) => Err(format!("can't cast {ty:?} to {self:?}")),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FieldsHint {
pub known_fields: Interned<[FieldType<Interned<dyn TypeHintTrait>>]>,
pub more_fields: bool,
}
impl FieldsHint {
pub fn new(
known_fields: impl IntoIterator<Item = FieldType<Interned<dyn TypeHintTrait>>>,
more_fields: bool,
) -> Self {
let known_fields = Intern::intern_owned(Vec::from_iter(known_fields));
Self {
known_fields,
more_fields,
}
}
pub fn check_field(self, index: usize, field: FieldType<&dyn DynType>) -> Result<(), String> {
let Some(&known_field) = self.known_fields.get(index) else {
return if self.more_fields {
Ok(())
} else {
Err(format!(
"too many fields: name={:?} index={index}",
field.name
))
};
};
let FieldType {
name: known_name,
flipped: known_flipped,
ty: type_hint,
} = known_field;
let FieldType { name, flipped, ty } = field;
if name != known_name {
Err(format!(
"wrong field name {name:?}, expected {known_name:?}"
))
} else if flipped != known_flipped {
Err(format!(
"wrong field direction: flipped={flipped:?}, expected flipped={known_flipped}"
))
} else {
type_hint.matches(ty)
}
}
}
pub trait BundleType:
Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref + Connect<Self>
where
Self::Value: BundleValue + ToExpr<Type = Self>,
{
type Builder;
fn builder() -> Self::Builder;
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]>;
fn fields_hint() -> FieldsHint;
}
pub trait BundleValue: Value
where
<Self as ToExpr>::Type: BundleType<Value = Self>,
{
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> {
fn clone(&self) -> Self {
*self
macro_rules! impl_tuples {
([$({#[num = $num:literal] $var:ident: $T:ident})*] []) => {
impl<$($T: Type,)*> Type for ($($T,)*) {
fn canonical(&self) -> CanonicalType {
let ($($var,)*) = self;
Bundle::new(&[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*]).canonical()
}
}
impl<T> Copy for ToBitsMemoize<T> {}
impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
type Input = T;
type InputOwned = T;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let input = input.to_canonical();
let mut bits = BitVec::with_capacity(input.ty.bit_width());
for field in input.fields.iter() {
bits.extend_from_bitslice(&field.to_bits());
}
Intern::intern_owned(bits)
}
}
ToBitsMemoize::<Self>(PhantomData).get(this)
}
}
pub struct DynBundleMatch;
impl Type for DynBundleType {
type CanonicalType = DynBundleType;
type Value = DynBundle;
type CanonicalValue = DynBundle;
type MaskType = DynBundleType;
type MaskValue = DynBundle;
type MatchVariant = DynBundleMatch;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = this;
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(DynBundleMatch))
}
fn mask_type(&self) -> Self::MaskType {
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
struct Impl;
impl Memoize for Impl {
type Input = DynBundleType;
type InputOwned = DynBundleType;
type Output = DynBundleType;
fn inner(self, input: &Self::Input) -> Self::Output {
DynBundleType::new(Intern::intern_owned(Vec::from_iter(
input
.fields()
.iter()
.map(|&FieldType { name, flipped, ty }| FieldType {
name,
flipped,
ty: ty.mask_type().canonical(),
}),
)))
}
}
Impl.get(self)
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
pub struct NoBuilder;
impl TypeWithDeref for DynBundleType {
fn expr_deref(this: &Expr<Self::Value>) -> &Self::MatchVariant {
let _ = this;
&DynBundleMatch
}
}
impl Connect<Self> for DynBundleType {}
impl BundleType for DynBundleType {
type Builder = NoBuilder;
fn builder() -> Self::Builder {
NoBuilder
}
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
self.0.fields
}
fn fields_hint() -> FieldsHint {
FieldsHint {
known_fields: [][..].intern(),
more_fields: true,
}
}
}
impl CanonicalType for DynBundleType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::BundleType;
}
impl ToExpr for DynBundle {
type Type = DynBundleType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynBundle {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl BundleValue for DynBundle {}
impl CanonicalValue for DynBundle {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Bundle(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
macro_rules! impl_tuple_builder {
($builder:ident, [
$(($before_Ts:ident $before_fields:ident $before_members:literal))*
] [
($T:ident $field:ident $m:literal)
$(($after_Ts:ident $after_fields:ident $after_members:literal))*
]) => {
impl_tuple_builder!($builder, [
$(($before_Ts $before_fields $before_members))*
($T $field $m)
] [
$(($after_Ts $after_fields $after_members))*
]);
impl<Phantom, $($before_Ts,)* $($after_Ts,)*> $builder<
Phantom,
$($before_Ts,)*
(),
$($after_Ts,)*
> {
pub fn $field<$T: ToExpr>(self, $field: $T) -> $builder<
Phantom,
$($before_Ts,)*
Expr<<$T::Type as Type>::Value>,
$($after_Ts,)*
> {
let Self {
$($before_fields,)*
$field: _,
$($after_fields, )*
_phantom: _,
} = self;
let $field = $field.to_expr();
$builder {
$($before_fields,)*
$field,
$($after_fields,)*
_phantom: PhantomData,
}
}
}
};
($builder:ident, [$($before:tt)*] []) => {};
}
macro_rules! into_unit {
($($tt:tt)*) => {
()
};
}
macro_rules! impl_tuple {
($builder:ident, $(($T:ident $T2:ident $field:ident $m:tt)),*) => {
pub struct $builder<Phantom, $($T),*> {
$($field: $T,)*
_phantom: PhantomData<Phantom>,
}
impl_tuple_builder!($builder, [] [$(($T $field $m))*]);
impl<$($T: Value),*> $builder<($($T,)*), $(Expr<$T>,)*>
where
$($T::Type: Type<Value = $T>,)*
{
pub fn build(self) -> Expr<($($T,)*)> {
let Self {
$($field,)*
_phantom: _,
} = self;
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
}
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
type Type = ($($T::Type,)*);
#[allow(clippy::unused_unit)]
fn ty(&self) -> Self::Type {
let ($($field,)*) = self;
($($field.ty(),)*)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let ($($field,)*) = self;
$(let $field = $field.to_expr();)*
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
}
}
impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*)
where
$($T: Connect<$T2>,)*
{
}
impl<$($T: Type,)*> Type for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type CanonicalType = DynBundleType;
type Value = ($($T::Value,)*);
type CanonicalValue = DynBundle;
type MaskType = ($($T::MaskType,)*);
type MaskValue = ($($T::MaskValue,)*);
type MatchVariant = ($(Expr<$T::Value>,)*);
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = this;
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(($(this.field(stringify!($m)),)*)))
}
#[allow(clippy::unused_unit)]
fn mask_type(&self) -> Self::MaskType {
let ($($field,)*) = self;
($($field.mask_type(),)*)
}
fn canonical(&self) -> Self::CanonicalType {
DynBundleType::new(self.fields())
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(self.canonical())
}
#[allow(clippy::unused_unit)]
fn from_canonical_type(t: Self::CanonicalType) -> Self {
let [$($field),*] = *t.fields() else {
panic!("wrong number of fields");
};
($($field.from_canonical_type_helper(stringify!($m), false),)*)
}
}
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
fn expr_deref(
this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
let _ = this;
Interned::<_>::into_inner(
Intern::intern_sized((
$(this.field(stringify!($m)),)*
)),
)
}
}
impl<$($T: Type,)*> BundleType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type Builder = $builder<($($T::Value,)*), $(into_unit!($T),)*>;
fn builder() -> Self::Builder {
$builder {
$($field: (),)*
_phantom: PhantomData,
}
}
fn fields(
&self,
) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
[
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: self.$m.canonical_dyn(),
},)*
][..].intern()
}
fn fields_hint() -> FieldsHint {
FieldsHint::new([
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: TypeHint::<$T>::intern_dyn(),
},)*
], false)
}
}
impl<$($T: StaticType<MaskType: StaticType>,)*> StaticType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
#[allow(clippy::unused_unit)]
impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
fn static_type() -> Self {
($($T::static_type(),)*)
$(let $var = $T::static_type();)*
($($var,)*)
}
}
impl<$($T: Value,)*> Value for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
let ty = self.ty().canonical();
DynBundle::new(
ty,
Arc::new([
$(self.$m.to_canonical_dyn(),)*
]),
)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl<$($T: Value,)*> BundleValue for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]);
};
}
impl_tuple!(TupleBuilder0,);
impl_tuple!(TupleBuilder1, (A A2 field_0 0));
impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1));
impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2));
impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3));
impl_tuple!(TupleBuilder5, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4));
impl_tuple!(TupleBuilder6, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5));
impl_tuple!(TupleBuilder7, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6));
impl_tuple!(TupleBuilder8, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7));
impl_tuple!(TupleBuilder9, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8));
impl_tuple!(TupleBuilder10, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9));
impl_tuple!(TupleBuilder11, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10));
impl_tuple!(TupleBuilder12, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10), (L L2 field_11 11));
impl_tuples! {
[] [
{#[num = 0] a: A}
{#[num = 1] b: B}
{#[num = 2] c: C}
{#[num = 3] d: D}
{#[num = 4] e: E}
{#[num = 5] f: F}
{#[num = 6] g: G}
{#[num = 7] h: H}
{#[num = 8] i: I}
{#[num = 9] j: J}
{#[num = 10] k: K}
{#[num = 11] l: L}
]
}

View file

@ -1,620 +1,91 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{
bundle::{BundleValue, TypeHintTrait},
expr::{ops::VariantAccess, Expr, ToExpr},
int::{UInt, UIntType},
intern::{Intern, Interned, MemoizeGeneric},
module::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
NormalModule, Scope,
},
source_location::SourceLocation,
ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
},
};
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap;
use std::{
borrow::Cow,
fmt,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
intern::{Intern, Interned},
ty::{CanonicalType, StaticType, Type},
};
use hashbrown::HashSet;
use std::ops::Index;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct VariantType<T> {
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EnumVariant {
pub name: Interned<str>,
pub ty: Option<T>,
pub ty: Option<CanonicalType>,
}
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let VariantType { name, ref ty } = *self.0;
if let Some(ty) = ty {
write!(f, "{name}({ty:?})")
} else {
write!(f, "{name}")
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct Enum {
variants: Interned<[EnumVariant]>,
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> VariantType<T> {
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType { name, ty: f(ty) }
}
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType {
name,
ty: ty.map(f),
}
}
pub fn as_ref_ty(&self) -> VariantType<&T> {
VariantType {
name: self.name,
ty: self.ty.as_ref(),
}
}
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
FmtDebugInEnum(self)
}
}
impl<T: Type> VariantType<T> {
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
self.as_ref_ty().map_ty(T::canonical)
}
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
self.as_ref_ty().map_ty(T::to_dyn)
}
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.as_ref_ty().map_ty(T::canonical_dyn)
}
}
impl VariantType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
let Some(ty) = self.ty else {
panic!("variant {expected_name} has no value but a value is expected");
};
T::from_dyn_canonical_type(ty)
}
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
assert!(
self.ty.is_none(),
"variant {expected_name} has a value but is expected to have no value"
);
}
}
#[derive(Clone, Eq)]
struct DynEnumTypeImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize,
is_storable: bool,
is_castable_from_bits: bool,
}
impl fmt::Debug for DynEnumTypeImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DynEnumType ")?;
f.debug_set()
.entries(
self.variants
.iter()
.map(|variant| variant.fmt_debug_in_enum()),
)
.finish()
}
}
impl PartialEq for DynEnumTypeImpl {
fn eq(&self, other: &Self) -> bool {
self.variants == other.variants
}
}
impl Hash for DynEnumTypeImpl {
fn hash<H: Hasher>(&self, state: &mut H) {
self.variants.hash(state);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
impl fmt::Debug for DynEnumType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn discriminant_bit_width_impl(variant_count: usize) -> usize {
variant_count
.next_power_of_two()
.checked_ilog2()
.unwrap_or(0) as usize
}
impl DynEnumType {
impl Enum {
#[track_caller]
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
let mut name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize;
let mut is_storable = true;
let mut is_castable_from_bits = true;
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
}
if let Some(ty) = ty {
assert!(
ty.is_passive(),
"variant type must be a passive type: {ty:?}"
);
body_bit_width = body_bit_width.max(ty.bit_width());
is_storable &= ty.is_storable();
is_castable_from_bits &= ty.is_castable_from_bits();
}
pub fn new(variants: &[EnumVariant]) -> Self {
let variants = variants.intern();
let mut names = HashSet::new();
for variant in variants {
assert!(
names.insert(variant.name),
"duplicate variant name: {:?}",
variant.name
);
}
let bit_width = body_bit_width
.checked_add(discriminant_bit_width_impl(variants.len()))
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
Self(
DynEnumTypeImpl {
variants,
name_indexes,
bit_width,
is_storable,
is_castable_from_bits,
}
.intern_sized(),
)
}
pub fn discriminant_bit_width(self) -> usize {
discriminant_bit_width_impl(self.variants().len())
}
pub fn is_passive(self) -> bool {
true
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
}
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes
Self { variants }
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynEnum {
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
}
impl DynEnum {
#[track_caller]
pub fn new_by_index(
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant = ty.variants()[variant_index];
assert_eq!(
variant_value.as_ref().map(|v| v.ty()),
variant.ty,
"variant value doesn't match type"
);
Self {
ty,
variant_index,
variant_value,
}
}
#[track_caller]
pub fn new_by_name(
ty: DynEnumType,
variant_name: Interned<str>,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant_index = ty.name_indexes()[&variant_name];
Self::new_by_index(ty, variant_index, variant_value)
}
pub fn variant_index(&self) -> usize {
self.variant_index
}
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
&self.variant_value
}
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.ty.variants()[self.variant_index]
}
pub fn variant_name(&self) -> Interned<str> {
self.variant_with_type().name
}
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
self.variant_with_type().ty
}
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
self.variant_with_type()
.map_opt_ty(|_| self.variant_value.as_ref())
impl Type for Enum {
fn canonical(&self) -> CanonicalType {
CanonicalType::Enum(*self)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct VariantsHint {
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
pub more_variants: bool,
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct HdlOption<T: Type> {
some_ty: T,
}
impl VariantsHint {
pub fn new(
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
more_variants: bool,
) -> Self {
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
Self {
known_variants,
more_variants,
}
impl<T: Type> HdlOption<T> {
pub const fn new(some_ty: T) -> Self {
Self { some_ty }
}
pub fn check_variant(
self,
index: usize,
variant: VariantType<&dyn DynType>,
) -> Result<(), String> {
let Some(&known_variant) = self.known_variants.get(index) else {
return if self.more_variants {
Ok(())
} else {
Err(format!(
"too many variants: name={:?} index={index}",
variant.name
))
};
};
let VariantType {
name: known_name,
ty: type_hint,
} = known_variant;
let VariantType { name, ty } = variant;
if name != known_name {
Err(format!(
"wrong variant name {name:?}, expected {known_name:?}"
))
} else {
match (ty, type_hint) {
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
(None, None) => Ok(()),
(None, Some(_)) => Err(format!(
"expected variant {name:?} to have type, no type provided"
)),
(Some(_), None) => Err(format!(
"expected variant {name:?} to have no type, but a type was provided"
)),
}
}
pub const fn some_ty(&self) -> &T {
&self.some_ty
}
}
pub trait EnumType:
Type<
CanonicalType = DynEnumType,
CanonicalValue = DynEnum,
MaskType = UIntType<1>,
MaskValue = UInt<1>,
MatchActiveScope = Scope,
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MatchVariantsIter = EnumMatchVariantsIter<Self>,
> + Connect<Self>
where
Self::Value: EnumValue + ToExpr<Type = Self>,
{
type Builder;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope);
fn builder() -> Self::Builder;
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
fn variants_hint() -> VariantsHint;
#[allow(clippy::result_unit_err)]
fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
&self,
variant_index: usize,
variant_value: Option<&VariantValue>,
) -> Result<Interned<BitSlice>, ()> {
#[derive(Hash, Eq, PartialEq)]
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
impl<E, V> Clone for VariantToBitsMemoize<E, V> {
fn clone(&self) -> Self {
*self
}
}
impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
impl<
E: EnumType<Value: EnumValue<Type = E>>,
V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
> MemoizeGeneric for VariantToBitsMemoize<E, V>
{
type InputRef<'a> = (&'a E, usize, Option<&'a V>);
type InputOwned = (E, usize, Option<V>);
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
type Output = Result<Interned<BitSlice>, ()>;
#[allow(non_upper_case_globals)]
pub const HdlOption: HdlOptionWithoutGenerics = HdlOptionWithoutGenerics;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
(&input.0, input.1, input.2.as_ref())
}
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
(&input.0, input.1, input.2.as_deref())
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
(Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
let (ty, variant_index, variant_value) = input;
let ty = ty.canonical();
let mut bits = BitVec::with_capacity(ty.bit_width());
bits.extend_from_bitslice(
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
);
if let Some(variant_value) = variant_value {
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
}
bits.resize(ty.bit_width(), false);
Ok(Intern::intern_owned(bits))
}
}
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
self,
variant_index,
variant_value,
))
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
pub struct HdlOptionWithoutGenerics;
impl<T: Type> Index<T> for HdlOptionWithoutGenerics {
type Output = HdlOption<T>;
fn index(&self, some_ty: T) -> &Self::Output {
Interned::<_>::into_inner(Intern::intern_sized(HdlOption::new(some_ty)))
}
}
pub trait EnumValue: Value
where
<Self as ToExpr>::Type: EnumType<Value = Self>,
{
}
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
where
T::Value: EnumValue<Type = T>;
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
type MatchVariant = T::MatchVariant;
type MatchActiveScope = Scope;
fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope) {
T::match_activate_scope(self)
impl<T: Type> Type for HdlOption<T> {
fn canonical(&self) -> CanonicalType {
CanonicalType::Enum(Enum::new(&[
EnumVariant {
name: "None".intern(),
ty: None,
},
EnumVariant {
name: "Some".intern(),
ty: Some(self.some_ty().canonical()),
},
]))
}
}
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
self.0.variant_access()
}
pub fn activate(
self,
) -> (
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
Scope,
) {
self.0.activate()
}
}
#[derive(Clone)]
pub struct EnumMatchVariantsIter<T: EnumType>
where
T::Value: EnumValue<Type = T>,
{
pub(crate) inner: EnumMatchVariantsIterImpl<T>,
pub(crate) variant_index: std::ops::Range<usize>,
}
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
type Item = EnumMatchVariantAndInactiveScope<T>;
fn next(&mut self) -> Option<Self::Item> {
self.variant_index.next().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.variant_index.size_hint()
}
}
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
fn len(&self) -> usize {
self.variant_index.len()
}
}
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {}
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T>
where
T::Value: EnumValue<Type = T>,
{
fn next_back(&mut self) -> Option<Self::Item> {
self.variant_index.next_back().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
})
}
}
impl Type for DynEnumType {
type CanonicalType = DynEnumType;
type Value = DynEnum;
type CanonicalValue = DynEnum;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
type MatchVariant = Option<Expr<DynCanonicalValue>>;
type MatchActiveScope = Scope;
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: crate::bundle::BundleType<Value = IO>,
{
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::EnumType(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for DynEnumType {}
pub struct NoBuilder;
impl EnumType for DynEnumType {
type Builder = NoBuilder;
fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope) {
let (expr, scope) = v.0.activate();
(expr.variant_type().ty.map(|_| expr.to_expr()), scope)
}
fn builder() -> Self::Builder {
NoBuilder
}
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
self.0.variants
}
fn variants_hint() -> VariantsHint {
VariantsHint {
known_variants: [][..].intern(),
more_variants: true,
}
}
}
impl CanonicalType for DynEnumType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType;
}
impl ToExpr for DynEnum {
type Type = DynEnumType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynEnum {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
impl EnumValue for DynEnum {}
impl CanonicalValue for DynEnum {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Enum(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap()
}
}
mod impl_option {
#[allow(dead_code)]
#[derive(crate::ty::Value)]
#[hdl(target(std::option::Option), connect_inexact, outline_generated)]
pub enum Option<T> {
None,
Some(T),
impl<T: StaticType> StaticType for HdlOption<T> {
fn static_type() -> Self {
HdlOption::new(T::static_type())
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -24,22 +24,23 @@ pub use fayalite_proc_macros::hdl_module;
#[cfg(feature = "unstable-doc")]
pub mod _docs;
pub mod annotations;
// FIXME: finish
//pub mod annotations;
pub mod array;
pub mod bundle;
pub mod cli;
pub mod clock;
//pub mod cli;
//pub mod clock;
pub mod enum_;
pub mod expr;
pub mod firrtl;
//pub mod firrtl;
pub mod int;
pub mod intern;
pub mod memory;
pub mod module;
pub mod reg;
pub mod reset;
//pub mod memory;
//pub mod module;
//pub mod reg;
//pub mod reset;
pub mod source_location;
pub mod ty;
pub mod util;
pub mod valueless;
pub mod wire;
//pub mod valueless;
//pub mod wire;

File diff suppressed because it is too large Load diff

View file

@ -3,10 +3,13 @@
mod const_bool;
mod const_cmp;
mod const_usize;
mod misc;
#[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
#[doc(inline)]
pub use const_usize::{ConstUsize, GenericConstUsize};
#[doc(inline)]
pub use const_cmp::{

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{fmt::Debug, hash::Hash};
mod sealed {
pub trait Sealed {}
}
/// the only implementation is `ConstUsize<Self::VALUE>`
pub trait GenericConstUsize:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: usize;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstUsize<const VALUE: usize>;
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
}
}
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
const VALUE: usize = VALUE;
}