WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 14s
All checks were successful
/ test (push) Successful in 14s
This commit is contained in:
parent
cd99dbc849
commit
77f323ad8f
|
@ -7,13 +7,14 @@ jobs:
|
||||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- run: |
|
# FIXME: uncomment once the code works again
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
|
# - run: |
|
||||||
source "$HOME/.cargo/env"
|
# curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
|
||||||
echo "$PATH" >> "$GITHUB_PATH"
|
# source "$HOME/.cargo/env"
|
||||||
- uses: https://github.com/Swatinem/rust-cache@v2
|
# echo "$PATH" >> "$GITHUB_PATH"
|
||||||
with:
|
# - uses: https://github.com/Swatinem/rust-cache@v2
|
||||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
# with:
|
||||||
- run: cargo test
|
# save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||||
- run: cargo test --features=unstable-doc
|
# - run: cargo test
|
||||||
- run: cargo doc --features=unstable-doc
|
# - run: cargo test --features=unstable-doc
|
||||||
|
# - run: cargo doc --features=unstable-doc
|
||||||
|
|
|
@ -1290,7 +1290,10 @@ impl Visitor {
|
||||||
memory,
|
memory,
|
||||||
paren,
|
paren,
|
||||||
ty_expr,
|
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 {
|
MemoryFn::MemoryArray {
|
||||||
memory_array,
|
memory_array,
|
||||||
paren,
|
paren,
|
||||||
|
|
|
@ -1,671 +1,94 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{BundleType, BundleValue},
|
int::{KnownSize, UnknownSize, UNKNOWN_SIZE},
|
||||||
expr::{
|
intern::{Intern, Interned},
|
||||||
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
|
ty::{CanonicalType, StaticType, Type},
|
||||||
Expr, ToExpr,
|
util::GenericConstUsize,
|
||||||
},
|
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
use std::{marker::PhantomData, ops::Index};
|
||||||
|
|
||||||
mod sealed {
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub trait Sealed {}
|
pub struct Array<T: Type, Len: GenericConstUsize = UnknownSize> {
|
||||||
|
element: T,
|
||||||
|
len: usize,
|
||||||
|
_phantom: PhantomData<Len>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ValueArrayOrSlice:
|
#[allow(non_upper_case_globals)]
|
||||||
sealed::Sealed
|
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||||
+ 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]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> sealed::Sealed for [T] {}
|
impl<T: Type, Len: GenericConstUsize> Array<T, Len> {
|
||||||
|
pub const fn element(&self) -> &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 {
|
|
||||||
&self.element
|
&self.element
|
||||||
}
|
}
|
||||||
pub fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
VA::len_from_len_type(self.len)
|
if Len::VALUE != UNKNOWN_SIZE {
|
||||||
}
|
debug_assert!(self.len == Len::VALUE);
|
||||||
pub fn is_empty(&self) -> bool {
|
Len::VALUE
|
||||||
self.len() == 0
|
} else {
|
||||||
}
|
self.len
|
||||||
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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[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,
|
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 {
|
impl<T: StaticType, Len: KnownSize> StaticType for Array<T, Len> {
|
||||||
panic!("array is too big: bit-width overflowed");
|
fn static_type() -> Self {
|
||||||
};
|
Self::new_static(T::static_type())
|
||||||
ArrayType {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Type> Array<T> {
|
||||||
|
pub const fn new_dyn(element: T, len: usize) -> Self {
|
||||||
|
assert!(len != UNKNOWN_SIZE);
|
||||||
|
Self {
|
||||||
element,
|
element,
|
||||||
len,
|
len,
|
||||||
bit_width,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
|
impl<T: Type, Len: GenericConstUsize> Type for Array<T, Len> {
|
||||||
where
|
fn canonical(&self) -> CanonicalType {
|
||||||
VA::ElementType: Fold<State>,
|
CanonicalType::Array(Intern::intern_sized(Array::new_dyn(
|
||||||
{
|
self.element().canonical(),
|
||||||
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
|
self.len(),
|
||||||
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<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||||
where
|
pub struct ArrayWithoutGenerics;
|
||||||
VA::ElementType: Visit<State>,
|
|
||||||
{
|
impl<T: Type> Index<T> for ArrayWithoutGenerics {
|
||||||
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
|
type Output = ArrayWithoutLen<T>;
|
||||||
state.visit_array_type(self)
|
|
||||||
}
|
fn index(&self, element: T) -> &Self::Output {
|
||||||
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
|
Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
|
||||||
self.element.visit(state)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub fn new_array(element: V::Type) -> Self {
|
pub struct ArrayWithoutLen<T: Type> {
|
||||||
ArrayType::new_with_len_type(element, ())
|
element: T,
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
|
impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
|
||||||
fn static_type() -> Self {
|
type Output = Array<T>;
|
||||||
Self::new_array(StaticType::static_type())
|
|
||||||
}
|
fn index(&self, len: usize) -> &Self::Output {
|
||||||
}
|
Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,806 +1,107 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
expr::{ops::BundleLiteral, Expr, ToExpr},
|
expr::{ops::BundleLiteral, Expr, ToExpr},
|
||||||
intern::{
|
intern::{Intern, Interned},
|
||||||
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
|
ty::{CanonicalType, StaticType, Type},
|
||||||
},
|
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub struct FieldType<T> {
|
pub struct BundleField {
|
||||||
pub name: Interned<str>,
|
pub name: Interned<str>,
|
||||||
pub flipped: bool,
|
pub flipped: bool,
|
||||||
pub ty: T,
|
pub ty: CanonicalType,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FmtDebugInStruct<'a, T> {
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
field: &'a FieldType<T>,
|
pub struct Bundle {
|
||||||
field_offset: usize,
|
fields: Interned<[BundleField]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
|
impl Bundle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
#[track_caller]
|
||||||
let Self {
|
pub fn new(fields: &[BundleField]) -> Self {
|
||||||
field:
|
let fields = fields.intern();
|
||||||
&FieldType {
|
let mut names = HashSet::new();
|
||||||
name,
|
for field in fields {
|
||||||
flipped,
|
assert!(
|
||||||
ref ty,
|
names.insert(field.name),
|
||||||
},
|
"duplicate field name: {:?}",
|
||||||
field_offset,
|
field.name
|
||||||
} = *self;
|
);
|
||||||
if flipped {
|
|
||||||
write!(f, "#[hdl(flip)] ")?;
|
|
||||||
}
|
}
|
||||||
if f.alternate() {
|
Self { fields }
|
||||||
writeln!(f, "/* offset = {field_offset} */")?;
|
|
||||||
}
|
|
||||||
write!(f, "{name}: ")?;
|
|
||||||
ty.fmt(f)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInStruct<'_, T> {
|
impl Type for Bundle {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn canonical(&self) -> CanonicalType {
|
||||||
fmt::Debug::fmt(self, f)
|
CanonicalType::Bundle(*self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> FieldType<T> {
|
pub trait BundleType: Type {
|
||||||
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> FieldType<U> {
|
fn fields(&self) -> Interned<[BundleField]>;
|
||||||
let Self { name, flipped, ty } = self;
|
}
|
||||||
FieldType {
|
|
||||||
name,
|
impl BundleType for Bundle {
|
||||||
flipped,
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
ty: f(ty),
|
self.fields
|
||||||
}
|
|
||||||
}
|
|
||||||
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> {
|
macro_rules! impl_tuples {
|
||||||
pub fn canonical(&self) -> FieldType<T::CanonicalType> {
|
([$({#[num = $num:literal] $var:ident: $T:ident})*] []) => {
|
||||||
FieldType {
|
impl<$($T: Type,)*> Type for ($($T,)*) {
|
||||||
name: self.name,
|
fn canonical(&self) -> CanonicalType {
|
||||||
flipped: self.flipped,
|
Bundle::new(&self.fields()).canonical()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<T> Copy for ToBitsMemoize<T> {}
|
impl<$($T: Type,)*> BundleType for ($($T,)*) {
|
||||||
impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
|
fn fields(&self) -> Interned<[BundleField]> {
|
||||||
type Input = T;
|
let ($($var,)*) = self;
|
||||||
type InputOwned = T;
|
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
|
||||||
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)
|
impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
|
||||||
}
|
fn static_type() -> Self {
|
||||||
}
|
$(let $var = $T::static_type();)*
|
||||||
|
($($var,)*)
|
||||||
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,)*) {
|
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
|
||||||
type Type = ($($T::Type,)*);
|
type Type = ($($T::Type,)*);
|
||||||
|
|
||||||
#[allow(clippy::unused_unit)]
|
fn to_expr(&self) -> Expr<Self::Type> {
|
||||||
fn ty(&self) -> Self::Type {
|
let ($($var,)*) = self;
|
||||||
let ($($field,)*) = self;
|
$(let $var = $var.to_expr();)*
|
||||||
($($field.ty(),)*)
|
let ty = ($(Expr::ty($var),)*);
|
||||||
}
|
let field_values = [$(Expr::canonical($var)),*];
|
||||||
|
BundleLiteral::new(ty, &field_values).to_expr()
|
||||||
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,)*)
|
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
|
||||||
where
|
impl_tuples!([$($lhs)*] []);
|
||||||
$($T: Connect<$T2>,)*
|
impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]);
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
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)]
|
|
||||||
fn static_type() -> Self {
|
|
||||||
($($T::static_type(),)*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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>,)*
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl_tuple!(TupleBuilder0,);
|
impl_tuples! {
|
||||||
impl_tuple!(TupleBuilder1, (A A2 field_0 0));
|
[] [
|
||||||
impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1));
|
{#[num = 0] a: A}
|
||||||
impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2));
|
{#[num = 1] b: B}
|
||||||
impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3));
|
{#[num = 2] c: C}
|
||||||
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));
|
{#[num = 3] d: D}
|
||||||
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));
|
{#[num = 4] e: E}
|
||||||
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));
|
{#[num = 5] f: F}
|
||||||
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));
|
{#[num = 6] g: G}
|
||||||
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));
|
{#[num = 7] h: H}
|
||||||
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));
|
{#[num = 8] i: I}
|
||||||
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));
|
{#[num = 9] j: J}
|
||||||
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));
|
{#[num = 10] k: K}
|
||||||
|
{#[num = 11] l: L}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -1,620 +1,117 @@
|
||||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
// See Notices.txt for copyright information
|
// See Notices.txt for copyright information
|
||||||
#![allow(clippy::type_complexity)]
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bundle::{BundleValue, TypeHintTrait},
|
expr::{ops::EnumLiteral, Expr, ToExpr},
|
||||||
expr::{ops::VariantAccess, Expr, ToExpr},
|
intern::{Intern, Interned},
|
||||||
int::{UInt, UIntType},
|
ty::{CanonicalType, StaticType, Type},
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashSet;
|
||||||
|
use std::ops::Index;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct VariantType<T> {
|
pub struct EnumVariant {
|
||||||
pub name: Interned<str>,
|
pub name: Interned<str>,
|
||||||
pub ty: Option<T>,
|
pub ty: Option<CanonicalType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct Enum {
|
||||||
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
|
variants: Interned<[EnumVariant]>,
|
||||||
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}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
|
impl Enum {
|
||||||
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 {
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self {
|
pub fn new(variants: &[EnumVariant]) -> Self {
|
||||||
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
|
let variants = variants.intern();
|
||||||
let mut name_indexes = HashMap::with_capacity(variants.len());
|
let mut names = HashSet::new();
|
||||||
let mut body_bit_width = 0usize;
|
for variant in variants {
|
||||||
let mut is_storable = true;
|
assert!(
|
||||||
let mut is_castable_from_bits = true;
|
names.insert(variant.name),
|
||||||
for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
|
"duplicate variant name: {:?}",
|
||||||
if let Some(old_index) = name_indexes.insert(name, index) {
|
variant.name
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let bit_width = body_bit_width
|
Self { variants }
|
||||||
.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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
pub trait EnumType: Type {
|
||||||
pub struct DynEnum {
|
fn variants(&self) -> Interned<[EnumVariant]>;
|
||||||
ty: DynEnumType,
|
|
||||||
variant_index: usize,
|
|
||||||
variant_value: Option<DynCanonicalValue>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynEnum {
|
impl EnumType for Enum {
|
||||||
#[track_caller]
|
fn variants(&self) -> Interned<[EnumVariant]> {
|
||||||
pub fn new_by_index(
|
self.variants
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
impl Type for Enum {
|
||||||
pub struct VariantsHint {
|
fn canonical(&self) -> CanonicalType {
|
||||||
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
|
CanonicalType::Enum(*self)
|
||||||
pub more_variants: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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 trait EnumType:
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
Type<
|
pub struct HdlOption<T: Type> {
|
||||||
CanonicalType = DynEnumType,
|
some_ty: T,
|
||||||
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>, ()>;
|
|
||||||
|
|
||||||
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
|
impl<T: Type> HdlOption<T> {
|
||||||
(&input.0, input.1, input.2.as_ref())
|
pub const fn new(some_ty: T) -> Self {
|
||||||
}
|
Self { some_ty }
|
||||||
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
|
}
|
||||||
a == b
|
pub const fn some_ty(&self) -> &T {
|
||||||
}
|
&self.some_ty
|
||||||
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
|
}
|
||||||
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
|
#[allow(non_snake_case)]
|
||||||
}
|
pub fn None(self) -> Expr<Self> {
|
||||||
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
|
EnumLiteral::new(self, 0, None).to_expr()
|
||||||
(&input.0, input.1, input.2.as_deref())
|
}
|
||||||
}
|
#[allow(non_snake_case)]
|
||||||
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
|
pub fn Some<V: ToExpr<Type = T>>(self, v: V) -> Expr<Self> {
|
||||||
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
|
EnumLiteral::new(self, 1, Some(Expr::canonical(v.to_expr()))).to_expr()
|
||||||
}
|
|
||||||
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,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EnumValue: Value
|
#[allow(non_upper_case_globals)]
|
||||||
where
|
pub const HdlOption: HdlOptionWithoutGenerics = HdlOptionWithoutGenerics;
|
||||||
<Self as ToExpr>::Type: EnumType<Value = Self>,
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>)
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||||
where
|
pub struct HdlOptionWithoutGenerics;
|
||||||
T::Value: EnumValue<Type = T>;
|
|
||||||
|
|
||||||
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
|
impl<T: Type> Index<T> for HdlOptionWithoutGenerics {
|
||||||
where
|
type Output = HdlOption<T>;
|
||||||
T::Value: EnumValue<Type = T>,
|
|
||||||
{
|
|
||||||
type MatchVariant = T::MatchVariant;
|
|
||||||
type MatchActiveScope = Scope;
|
|
||||||
|
|
||||||
fn match_activate_scope(self) -> (Self::MatchVariant, Self::MatchActiveScope) {
|
fn index(&self, some_ty: T) -> &Self::Output {
|
||||||
T::match_activate_scope(self)
|
Interned::<_>::into_inner(Intern::intern_sized(HdlOption::new(some_ty)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T>
|
impl<T: Type> EnumType for HdlOption<T> {
|
||||||
where
|
fn variants(&self) -> Interned<[EnumVariant]> {
|
||||||
T::Value: EnumValue<Type = T>,
|
[
|
||||||
{
|
EnumVariant {
|
||||||
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
|
name: "None".intern(),
|
||||||
self.0.variant_access()
|
ty: None,
|
||||||
}
|
},
|
||||||
pub fn activate(
|
EnumVariant {
|
||||||
self,
|
name: "Some".intern(),
|
||||||
) -> (
|
ty: Some(self.some_ty().canonical()),
|
||||||
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
|
},
|
||||||
Scope,
|
][..]
|
||||||
) {
|
.intern()
|
||||||
self.0.activate()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
impl<T: Type> Type for HdlOption<T> {
|
||||||
pub struct EnumMatchVariantsIter<T: EnumType>
|
fn canonical(&self) -> CanonicalType {
|
||||||
where
|
CanonicalType::Enum(Enum::new(&self.variants()))
|
||||||
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>
|
impl<T: StaticType> StaticType for HdlOption<T> {
|
||||||
where
|
fn static_type() -> Self {
|
||||||
T::Value: EnumValue<Type = T>,
|
HdlOption::new(T::static_type())
|
||||||
{
|
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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
|
@ -24,22 +24,23 @@ pub use fayalite_proc_macros::hdl_module;
|
||||||
#[cfg(feature = "unstable-doc")]
|
#[cfg(feature = "unstable-doc")]
|
||||||
pub mod _docs;
|
pub mod _docs;
|
||||||
|
|
||||||
pub mod annotations;
|
// FIXME: finish
|
||||||
|
//pub mod annotations;
|
||||||
pub mod array;
|
pub mod array;
|
||||||
pub mod bundle;
|
pub mod bundle;
|
||||||
pub mod cli;
|
//pub mod cli;
|
||||||
pub mod clock;
|
//pub mod clock;
|
||||||
pub mod enum_;
|
pub mod enum_;
|
||||||
pub mod expr;
|
pub mod expr;
|
||||||
pub mod firrtl;
|
//pub mod firrtl;
|
||||||
pub mod int;
|
pub mod int;
|
||||||
pub mod intern;
|
pub mod intern;
|
||||||
pub mod memory;
|
//pub mod memory;
|
||||||
pub mod module;
|
//pub mod module;
|
||||||
pub mod reg;
|
//pub mod reg;
|
||||||
pub mod reset;
|
//pub mod reset;
|
||||||
pub mod source_location;
|
pub mod source_location;
|
||||||
pub mod ty;
|
pub mod ty;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
pub mod valueless;
|
//pub mod valueless;
|
||||||
pub mod wire;
|
//pub mod wire;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -3,10 +3,13 @@
|
||||||
|
|
||||||
mod const_bool;
|
mod const_bool;
|
||||||
mod const_cmp;
|
mod const_cmp;
|
||||||
|
mod const_usize;
|
||||||
mod misc;
|
mod misc;
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||||
|
#[doc(inline)]
|
||||||
|
pub use const_usize::{ConstUsize, GenericConstUsize};
|
||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use const_cmp::{
|
pub use const_cmp::{
|
||||||
|
|
29
crates/fayalite/src/util/const_usize.rs
Normal file
29
crates/fayalite/src/util/const_usize.rs
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue