WIP adding type deduction
All checks were successful
/ test (push) Successful in 14m52s

This commit is contained in:
Jacob Lifshay 2024-08-01 00:22:46 -07:00
parent cd99dbc849
commit 47123eb36b
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
21 changed files with 855 additions and 427 deletions

View file

@ -815,6 +815,12 @@ impl ToTokens for ParsedEnum {
} }
} }
#[automatically_derived]
impl #static_type_impl_generics ::fayalite::ty::Connect<::fayalite::type_deduction::UndeducedType>
for #type_struct_ident #static_type_type_generics
#static_type_where_clause
{}
#[automatically_derived] #[automatically_derived]
impl #static_type_impl_generics ::fayalite::ty::Type impl #static_type_impl_generics ::fayalite::ty::Type
for #type_struct_ident #static_type_type_generics for #type_struct_ident #static_type_type_generics

View file

@ -522,6 +522,12 @@ impl ParsedStruct {
} }
} }
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Connect<::fayalite::type_deduction::UndeducedType>
for #type_struct_ident #type_generics
#where_clause
{}
#[automatically_derived] #[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics
#where_clause #where_clause

View file

@ -17,6 +17,7 @@ use crate::{
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType, DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
StaticValue, Type, TypeEnum, Value, ValueEnum, StaticValue, Type, TypeEnum, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
util::{ConstBool, GenericConstBool, MakeMutSlice}, util::{ConstBool, GenericConstBool, MakeMutSlice},
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use bitvec::{slice::BitSlice, vec::BitVec};
@ -195,7 +196,7 @@ where
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> { pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
element: VA::ElementType, element: VA::ElementType,
len: VA::LenType, len: VA::LenType,
bit_width: usize, bit_width: Result<usize, HitUndeducedType>,
} }
pub trait ArrayTypeTrait: pub trait ArrayTypeTrait:
@ -210,7 +211,6 @@ pub trait ArrayTypeTrait:
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> + Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>> + BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ sealed::Sealed + sealed::Sealed
+ Connect<Self>
{ {
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType> type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
+ ?Sized; + ?Sized;
@ -220,6 +220,8 @@ pub trait ArrayTypeTrait:
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {} impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> Connect<UndeducedType> for ArrayType<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> { impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
type ValueArrayOrSlice = VA; type ValueArrayOrSlice = VA;
type Element = VA::Element; type Element = VA::Element;
@ -248,7 +250,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.len() == 0 self.len() == 0
} }
pub fn bit_width(&self) -> usize { pub fn bit_width(&self) -> Result<usize, HitUndeducedType> {
self.bit_width self.bit_width
} }
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
@ -266,10 +268,18 @@ impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
) )
} }
#[track_caller] #[track_caller]
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self { fn get_bit_width(
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else { element: &VA::ElementType,
len: VA::LenType,
) -> Result<usize, HitUndeducedType> {
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()?) else {
panic!("array is too big: bit-width overflowed"); panic!("array is too big: bit-width overflowed");
}; };
Ok(bit_width)
}
#[track_caller]
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
let bit_width = Self::get_bit_width(&element, len);
ArrayType { ArrayType {
element, element,
len, len,
@ -437,7 +447,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
.collect(), .collect(),
} }
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
#[derive(Hash, Eq, PartialEq)] #[derive(Hash, Eq, PartialEq)]
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>); struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> { impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
@ -449,14 +459,14 @@ impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> { impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
type Input = Array<VA>; type Input = Array<VA>;
type InputOwned = Array<VA>; type InputOwned = Array<VA>;
type Output = Interned<BitSlice>; type Output = Result<Interned<BitSlice>, HitUndeducedType>;
fn inner(self, input: &Self::Input) -> Self::Output { fn inner(self, input: &Self::Input) -> Self::Output {
let mut bits = BitVec::with_capacity(input.ty().bit_width()); let mut bits = BitVec::with_capacity(input.ty().bit_width().unwrap_or(0));
for element in AsRef::<[_]>::as_ref(&*input.value).iter() { for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
bits.extend_from_bitslice(&element.to_bits()); bits.extend_from_bitslice(&element.to_bits()?);
} }
Intern::intern_owned(bits) Ok(Intern::intern_owned(bits))
} }
} }
ArrayToBitsMemoize::<VA>(PhantomData).get(this) ArrayToBitsMemoize::<VA>(PhantomData).get(this)
@ -467,7 +477,7 @@ impl CanonicalValue for Array<[DynCanonicalValue]> {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Array(this.clone()) ValueEnum::Array(this.clone())
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
Value::to_bits_impl(this) Value::to_bits_impl(this)
} }
} }

View file

@ -12,6 +12,7 @@ use crate::{
DynCanonicalValue, DynType, MatchVariantWithoutScope, StaticType, Type, TypeEnum, DynCanonicalValue, DynType, MatchVariantWithoutScope, StaticType, Type, TypeEnum,
TypeWithDeref, Value, ValueEnum, TypeWithDeref, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap; use hashbrown::HashMap;
@ -31,7 +32,7 @@ pub struct FieldType<T> {
pub struct FmtDebugInStruct<'a, T> { pub struct FmtDebugInStruct<'a, T> {
field: &'a FieldType<T>, field: &'a FieldType<T>,
field_offset: usize, field_offset: Option<usize>,
} }
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> { impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
@ -49,7 +50,9 @@ impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
write!(f, "#[hdl(flip)] ")?; write!(f, "#[hdl(flip)] ")?;
} }
if f.alternate() { if f.alternate() {
writeln!(f, "/* offset = {field_offset} */")?; if let Some(field_offset) = field_offset {
writeln!(f, "/* offset = {field_offset} */")?;
}
} }
write!(f, "{name}: ")?; write!(f, "{name}: ")?;
ty.fmt(f) ty.fmt(f)
@ -78,7 +81,7 @@ impl<T> FieldType<T> {
ty: &self.ty, ty: &self.ty,
} }
} }
pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> { pub fn fmt_debug_in_struct(&self, field_offset: Option<usize>) -> FmtDebugInStruct<'_, T> {
FmtDebugInStruct { FmtDebugInStruct {
field: self, field: self,
field_offset, field_offset,
@ -135,22 +138,19 @@ struct DynBundleTypeImpl {
fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>, fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
field_offsets: Interned<[usize]>, field_offsets: Interned<[usize]>,
is_passive: bool, is_passive: Result<bool, HitUndeducedType>,
is_storable: bool, is_storable: Result<bool, HitUndeducedType>,
is_castable_from_bits: bool, is_castable_from_bits: Result<bool, HitUndeducedType>,
bit_width: usize, bit_width: Result<usize, HitUndeducedType>,
} }
impl fmt::Debug for DynBundleTypeImpl { impl fmt::Debug for DynBundleTypeImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DynBundleType ")?; write!(f, "DynBundleType ")?;
f.debug_set() f.debug_set()
.entries( .entries(self.fields.iter().enumerate().map(|(index, field)| {
self.fields field.fmt_debug_in_struct(self.field_offsets.get(index).copied())
.iter() }))
.enumerate()
.map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
)
.finish() .finish()
} }
} }
@ -178,26 +178,39 @@ impl fmt::Debug for DynBundleType {
impl DynBundleType { impl DynBundleType {
pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self { pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self {
let is_passive = fields fn calc_prop(
.iter() fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>,
.all(|field| !field.flipped && field.ty.is_passive()); property: impl Fn(&Interned<dyn DynCanonicalType>) -> Result<bool, HitUndeducedType>,
let is_storable = fields ) -> Result<bool, HitUndeducedType> {
.iter() fields
.all(|field| !field.flipped && field.ty.is_storable()); .iter()
let is_castable_from_bits = fields .map(|field| {
.iter() if field.flipped {
.all(|field| !field.flipped && field.ty.is_castable_from_bits()); Ok(false)
} else {
property(&field.ty)
}
})
.fold(Ok(true), HitUndeducedType::reduce_and)
}
let is_passive = calc_prop(fields, DynType::is_passive);
let is_storable = calc_prop(fields, DynType::is_storable);
let is_castable_from_bits = calc_prop(fields, DynType::is_castable_from_bits);
let mut name_indexes = HashMap::with_capacity(fields.len()); let mut name_indexes = HashMap::with_capacity(fields.len());
let mut field_offsets = Vec::with_capacity(fields.len()); let mut field_offsets = Vec::with_capacity(fields.len());
let mut bit_width = 0usize; let mut bit_width = Ok(0usize);
for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() { for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) { if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate field name {name:?}: at both index {old_index} and {index}"); panic!("duplicate field name {name:?}: at both index {old_index} and {index}");
} }
field_offsets.push(bit_width); if let Ok(bit_width_value) = bit_width {
bit_width = bit_width field_offsets.push(bit_width_value);
.checked_add(ty.bit_width()) bit_width = ty.bit_width().map(|field_bit_width| {
.unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed")); bit_width_value
.checked_add(field_bit_width)
.unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed"))
});
}
} }
Self( Self(
DynBundleTypeImpl { DynBundleTypeImpl {
@ -212,16 +225,16 @@ impl DynBundleType {
.intern_sized(), .intern_sized(),
) )
} }
pub fn is_passive(self) -> bool { pub fn is_passive(self) -> Result<bool, HitUndeducedType> {
self.0.is_passive self.0.is_passive
} }
pub fn is_storable(self) -> bool { pub fn is_storable(self) -> Result<bool, HitUndeducedType> {
self.0.is_storable self.0.is_storable
} }
pub fn is_castable_from_bits(self) -> bool { pub fn is_castable_from_bits(self) -> Result<bool, HitUndeducedType> {
self.0.is_castable_from_bits self.0.is_castable_from_bits
} }
pub fn bit_width(self) -> usize { pub fn bit_width(self) -> Result<usize, HitUndeducedType> {
self.0.bit_width self.0.bit_width
} }
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
@ -370,7 +383,7 @@ impl FieldsHint {
} }
pub trait BundleType: pub trait BundleType:
Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref + Connect<Self> Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref
where where
Self::Value: BundleValue + ToExpr<Type = Self>, Self::Value: BundleValue + ToExpr<Type = Self>,
{ {
@ -384,7 +397,7 @@ pub trait BundleValue: Value
where where
<Self as ToExpr>::Type: BundleType<Value = Self>, <Self as ToExpr>::Type: BundleType<Value = Self>,
{ {
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
#[derive(Hash, Eq, PartialEq)] #[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>); struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> { impl<T> Clone for ToBitsMemoize<T> {
@ -396,21 +409,23 @@ where
impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> { impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
type Input = T; type Input = T;
type InputOwned = T; type InputOwned = T;
type Output = Interned<BitSlice>; type Output = Result<Interned<BitSlice>, HitUndeducedType>;
fn inner(self, input: &Self::Input) -> Self::Output { fn inner(self, input: &Self::Input) -> Self::Output {
let input = input.to_canonical(); let input = input.to_canonical();
let mut bits = BitVec::with_capacity(input.ty.bit_width()); let mut bits = BitVec::with_capacity(input.ty.bit_width().unwrap_or(0));
for field in input.fields.iter() { for field in input.fields.iter() {
bits.extend_from_bitslice(&field.to_bits()); bits.extend_from_bitslice(&field.to_bits()?);
} }
Intern::intern_owned(bits) Ok(Intern::intern_owned(bits))
} }
} }
ToBitsMemoize::<Self>(PhantomData).get(this) ToBitsMemoize::<Self>(PhantomData).get(this)
} }
} }
impl Connect<UndeducedType> for DynBundleType {}
pub struct DynBundleMatch; pub struct DynBundleMatch;
impl Type for DynBundleType { impl Type for DynBundleType {
@ -534,7 +549,7 @@ impl Value for DynBundle {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone() self.clone()
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
BundleValue::to_bits_impl(this) BundleValue::to_bits_impl(this)
} }
} }
@ -545,7 +560,7 @@ impl CanonicalValue for DynBundle {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Bundle(this.clone()) ValueEnum::Bundle(this.clone())
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
BundleValue::to_bits_impl(this) BundleValue::to_bits_impl(this)
} }
} }
@ -645,6 +660,8 @@ macro_rules! impl_tuple {
} }
} }
impl<$($T,)*> Connect<UndeducedType> for ($($T,)*) {}
impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*) impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*)
where where
$($T: Connect<$T2>,)* $($T: Connect<$T2>,)*
@ -778,7 +795,7 @@ macro_rules! impl_tuple {
]), ]),
) )
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
BundleValue::to_bits_impl(this) BundleValue::to_bits_impl(this)
} }
} }

View file

@ -10,6 +10,7 @@ use crate::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
util::interned_bit, util::interned_bit,
}; };
use bitvec::slice::BitSlice; use bitvec::slice::BitSlice;
@ -23,6 +24,8 @@ impl ClockType {
} }
} }
impl Connect<UndeducedType> for ClockType {}
impl Type for ClockType { impl Type for ClockType {
type Value = Clock; type Value = Clock;
type CanonicalType = ClockType; type CanonicalType = ClockType;
@ -88,8 +91,8 @@ impl Value for Clock {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self *self
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }
@ -97,8 +100,8 @@ impl CanonicalValue for Clock {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Clock(*this) ValueEnum::Clock(*this)
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }

View file

@ -3,7 +3,7 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
use crate::{ use crate::{
bundle::{BundleValue, TypeHintTrait}, bundle::{BundleValue, TypeHintTrait},
expr::{ops::VariantAccess, Expr, ToExpr}, expr::{ops::VariantAccess, Expr, NotALiteralExpr, ToExpr},
int::{UInt, UIntType}, int::{UInt, UIntType},
intern::{Intern, Interned, MemoizeGeneric}, intern::{Intern, Interned, MemoizeGeneric},
module::{ module::{
@ -15,6 +15,7 @@ use crate::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum, DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
}; };
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView}; use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap; use hashbrown::HashMap;
@ -107,9 +108,9 @@ impl VariantType<Interned<dyn DynCanonicalType>> {
struct DynEnumTypeImpl { struct DynEnumTypeImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize, bit_width: Result<usize, HitUndeducedType>,
is_storable: bool, is_storable: Result<bool, HitUndeducedType>,
is_castable_from_bits: bool, is_castable_from_bits: Result<bool, HitUndeducedType>,
} }
impl fmt::Debug for DynEnumTypeImpl { impl fmt::Debug for DynEnumTypeImpl {
@ -158,26 +159,28 @@ impl DynEnumType {
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { 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"); 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 name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize; let mut body_bit_width = Ok(0usize);
let mut is_storable = true; let mut is_storable = Ok(true);
let mut is_castable_from_bits = true; let mut is_castable_from_bits = Ok(true);
for (index, &VariantType { name, ty }) in variants.iter().enumerate() { for (index, &VariantType { name, ty }) in variants.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) { if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate variant name {name:?}: at both index {old_index} and {index}"); panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
} }
if let Some(ty) = ty { if let Some(ty) = ty {
assert!( assert!(
ty.is_passive(), ty.is_passive().unwrap_or(true),
"variant type must be a passive type: {ty:?}" "variant type must be a passive type: {ty:?}"
); );
body_bit_width = body_bit_width.max(ty.bit_width()); body_bit_width = body_bit_width.and_then(|v| Ok(v.max(ty.bit_width()?)));
is_storable &= ty.is_storable(); is_storable = HitUndeducedType::reduce_and(is_storable, ty.is_storable());
is_castable_from_bits &= ty.is_castable_from_bits(); is_castable_from_bits =
HitUndeducedType::reduce_and(is_castable_from_bits, ty.is_castable_from_bits());
} }
} }
let bit_width = body_bit_width let bit_width = body_bit_width.map(|v| {
.checked_add(discriminant_bit_width_impl(variants.len())) v.checked_add(discriminant_bit_width_impl(variants.len()))
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"))
});
Self( Self(
DynEnumTypeImpl { DynEnumTypeImpl {
variants, variants,
@ -192,16 +195,16 @@ impl DynEnumType {
pub fn discriminant_bit_width(self) -> usize { pub fn discriminant_bit_width(self) -> usize {
discriminant_bit_width_impl(self.variants().len()) discriminant_bit_width_impl(self.variants().len())
} }
pub fn is_passive(self) -> bool { pub fn is_passive(self) -> Result<bool, HitUndeducedType> {
true Ok(true)
} }
pub fn is_storable(self) -> bool { pub fn is_storable(self) -> Result<bool, HitUndeducedType> {
self.0.is_storable self.0.is_storable
} }
pub fn is_castable_from_bits(self) -> bool { pub fn is_castable_from_bits(self) -> Result<bool, HitUndeducedType> {
self.0.is_castable_from_bits self.0.is_castable_from_bits
} }
pub fn bit_width(self) -> usize { pub fn bit_width(self) -> Result<usize, HitUndeducedType> {
self.0.bit_width self.0.bit_width
} }
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
@ -323,14 +326,14 @@ impl VariantsHint {
pub trait EnumType: pub trait EnumType:
Type< Type<
CanonicalType = DynEnumType, CanonicalType = DynEnumType,
CanonicalValue = DynEnum, CanonicalValue = DynEnum,
MaskType = UIntType<1>, MaskType = UIntType<1>,
MaskValue = UInt<1>, MaskValue = UInt<1>,
MatchActiveScope = Scope, MatchActiveScope = Scope,
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MatchVariantsIter = EnumMatchVariantsIter<Self>, MatchVariantsIter = EnumMatchVariantsIter<Self>,
> + Connect<Self> >
where where
Self::Value: EnumValue + ToExpr<Type = Self>, Self::Value: EnumValue + ToExpr<Type = Self>,
{ {
@ -346,7 +349,7 @@ where
&self, &self,
variant_index: usize, variant_index: usize,
variant_value: Option<&VariantValue>, variant_value: Option<&VariantValue>,
) -> Result<Interned<BitSlice>, ()> { ) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
#[derive(Hash, Eq, PartialEq)] #[derive(Hash, Eq, PartialEq)]
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>); struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
impl<E, V> Clone for VariantToBitsMemoize<E, V> { impl<E, V> Clone for VariantToBitsMemoize<E, V> {
@ -363,7 +366,7 @@ where
type InputRef<'a> = (&'a E, usize, Option<&'a V>); type InputRef<'a> = (&'a E, usize, Option<&'a V>);
type InputOwned = (E, usize, Option<V>); type InputOwned = (E, usize, Option<V>);
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>); type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
type Output = Result<Interned<BitSlice>, ()>; type Output = Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr>;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> { fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
(&input.0, input.1, input.2.as_ref()) (&input.0, input.1, input.2.as_ref())
@ -386,15 +389,22 @@ where
fn inner(self, input: Self::InputRef<'_>) -> Self::Output { fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
let (ty, variant_index, variant_value) = input; let (ty, variant_index, variant_value) = input;
let ty = ty.canonical(); let ty = ty.canonical();
let mut bits = BitVec::with_capacity(ty.bit_width()); let bit_width = match ty.bit_width() {
Ok(v) => v,
Err(e) => return Ok(Err(e)),
};
let mut bits = BitVec::with_capacity(bit_width);
bits.extend_from_bitslice( bits.extend_from_bitslice(
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()], &variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
); );
if let Some(variant_value) = variant_value { if let Some(variant_value) = variant_value {
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?); match variant_value.to_expr().to_literal_bits()? {
Ok(variant_value) => bits.extend_from_bitslice(&variant_value),
Err(e) => return Ok(Err(e)),
}
} }
bits.resize(ty.bit_width(), false); bits.resize(bit_width, false);
Ok(Intern::intern_owned(bits)) Ok(Ok(Intern::intern_owned(bits)))
} }
} }
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get(( VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
@ -492,6 +502,8 @@ where
} }
} }
impl Connect<UndeducedType> for DynEnumType {}
impl Type for DynEnumType { impl Type for DynEnumType {
type CanonicalType = DynEnumType; type CanonicalType = DynEnumType;
type Value = DynEnum; type Value = DynEnum;
@ -589,7 +601,7 @@ impl Value for DynEnum {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone() self.clone()
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
this.ty this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref()) .variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap() .unwrap()
@ -602,7 +614,7 @@ impl CanonicalValue for DynEnum {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Enum(this.clone()) ValueEnum::Enum(this.clone())
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
this.ty this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref()) .variant_to_bits(this.variant_index, this.variant_value.as_ref())
.unwrap() .unwrap()

View file

@ -17,6 +17,7 @@ use crate::{
DynCanonicalType, DynCanonicalValue, DynType, DynValue, DynValueTrait, Type, TypeWithDeref, DynCanonicalType, DynCanonicalValue, DynType, DynValue, DynValueTrait, Type, TypeWithDeref,
Value, Value,
}, },
type_deduction::HitUndeducedType,
util::ConstBool, util::ConstBool,
valueless::Valueless, valueless::Valueless,
wire::Wire, wire::Wire,
@ -51,8 +52,7 @@ macro_rules! expr_enum {
$(Self::$Variant(v) => v.target(),)+ $(Self::$Variant(v) => v.target(),)+
} }
} }
#[allow(clippy::result_unit_err)] pub fn to_literal_bits(&self) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
pub fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
match self { match self {
$(Self::$Variant(v) => v.to_literal_bits(),)+ $(Self::$Variant(v) => v.to_literal_bits(),)+
} }
@ -176,6 +176,18 @@ pub struct Expr<T> {
__phantom: PhantomData<T>, __phantom: PhantomData<T>,
} }
#[derive(Debug, Copy, Clone)]
#[non_exhaustive]
pub struct NotALiteralExpr;
impl fmt::Display for NotALiteralExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Not a literal expression")
}
}
impl std::error::Error for NotALiteralExpr {}
impl<T> Expr<T> { impl<T> Expr<T> {
pub fn expr_enum(self) -> ExprEnum { pub fn expr_enum(self) -> ExprEnum {
self.__enum self.__enum
@ -210,8 +222,9 @@ impl<T> Expr<T> {
{ {
Valueless { ty: self.ty() } Valueless { ty: self.ty() }
} }
#[allow(clippy::result_unit_err)] pub fn to_literal_bits(
pub fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.expr_enum().to_literal_bits() self.expr_enum().to_literal_bits()
} }
#[track_caller] #[track_caller]
@ -796,8 +809,9 @@ pub trait ExprTrait: ExprTraitBase {
fn valueless(&self) -> Valueless<Self::Type> { fn valueless(&self) -> Valueless<Self::Type> {
Valueless { ty: self.ty() } Valueless { ty: self.ty() }
} }
#[allow(clippy::result_unit_err)] fn to_literal_bits(
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()>; &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr>;
} }
impl<T: ExprTrait> ExprTraitBase for T {} impl<T: ExprTrait> ExprTraitBase for T {}
@ -875,7 +889,7 @@ impl<T: Type> Literal<T> {
#[track_caller] #[track_caller]
pub fn new_unchecked(value: T::CanonicalValue) -> Self { pub fn new_unchecked(value: T::CanonicalValue) -> Self {
assert!( assert!(
value.ty().is_passive(), value.ty().is_passive().unwrap_or(true),
"can't have a literal with flipped fields" "can't have a literal with flipped fields"
); );
Self { value } Self { value }
@ -918,7 +932,9 @@ impl<T: Type> ExprTrait for Literal<T> {
None None
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Ok(self.value.to_bits()) Ok(self.value.to_bits())
} }
} }
@ -974,8 +990,10 @@ impl<T: Type> ExprTrait for ModuleIO<T> {
)) ))
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
Err(()) &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
} }
} }
@ -1008,8 +1026,10 @@ where
Some(Intern::intern_sized(self.canonical().into())) Some(Intern::intern_sized(self.canonical().into()))
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
Err(()) &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
} }
} }
@ -1024,8 +1044,10 @@ impl<T: Type> ExprTrait for Wire<T> {
Some(Intern::intern_sized(self.to_dyn_canonical_wire().into())) Some(Intern::intern_sized(self.to_dyn_canonical_wire().into()))
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
Err(()) &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
} }
} }
@ -1040,8 +1062,10 @@ impl<T: Type> ExprTrait for Reg<T> {
Some(Intern::intern_sized(self.to_dyn_canonical_reg().into())) Some(Intern::intern_sized(self.to_dyn_canonical_reg().into()))
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
Err(()) &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
} }
} }
@ -1089,7 +1113,9 @@ where
fn target(&self) -> Option<Interned<Target>> { fn target(&self) -> Option<Interned<Target>> {
Some(Intern::intern_sized(self.canonical().into())) Some(Intern::intern_sized(self.canonical().into()))
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
Err(()) &self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
} }
} }

View file

@ -6,12 +6,12 @@ use crate::{
clock::{Clock, ClockType, ToClock}, clock::{Clock, ClockType, ToClock},
enum_::{DynEnumType, EnumType, EnumValue, VariantType}, enum_::{DynEnumType, EnumType, EnumValue, VariantType},
expr::{ expr::{
sealed, Expr, ExprEnum, ExprTrait, Target, TargetPathArrayElement, TargetPathBundleField, sealed, Expr, ExprEnum, ExprTrait, NotALiteralExpr, Target, TargetPathArrayElement,
TargetPathDynArrayElement, TargetPathElement, ToExpr, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
}, },
int::{ int::{
DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, Int, DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, Int, IntCmp, IntType,
IntCmp, IntType, IntTypeTrait, IntValue, UInt, UIntType, IntTypeTrait, IntValue, StaticOrDynIntType, UInt, UIntType,
}, },
intern::{Intern, Interned}, intern::{Intern, Interned},
reset::{ reset::{
@ -19,9 +19,9 @@ use crate::{
ToReset, ToSyncReset, ToReset, ToSyncReset,
}, },
ty::{ ty::{
CanonicalType, CanonicalValue, DynCanonicalType, DynCanonicalValue, DynType, DynValueTrait, CanonicalType, CanonicalValue, DynCanonicalType, DynCanonicalValue, DynType, Type, Value,
Type, Value,
}, },
type_deduction::HitUndeducedType,
util::{interned_bit, ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}, util::{interned_bit, ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool},
valueless::{Valueless, ValuelessTr}, valueless::{Valueless, ValuelessTr},
}; };
@ -33,6 +33,43 @@ use std::{
ops::{self, Index, Range, RangeBounds}, ops::{self, Index, Range, RangeBounds},
}; };
fn unary_literal_bits<V: Value<Type: Type<Value = V>>>(
v: Expr<V>,
f: impl FnOnce(Interned<BitSlice>) -> Interned<BitSlice>,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Ok(v.to_literal_bits()?.map(f))
}
fn try_unary_literal_bits<V: Value<Type: Type<Value = V>>>(
v: Expr<V>,
f: impl FnOnce(Interned<BitSlice>) -> Result<Interned<BitSlice>, HitUndeducedType>,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Ok(v.to_literal_bits()?.and_then(f))
}
fn binary_literal_bits<L: Value<Type: Type<Value = L>>, R: Value<Type: Type<Value = R>>>(
l: Expr<L>,
r: Expr<R>,
f: impl FnOnce(Interned<BitSlice>, Interned<BitSlice>) -> Interned<BitSlice>,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
let l = l.to_literal_bits()?;
let r = r.to_literal_bits()?;
Ok((|| Ok(f(l?, r?)))())
}
fn try_binary_literal_bits<L: Value<Type: Type<Value = L>>, R: Value<Type: Type<Value = R>>>(
l: Expr<L>,
r: Expr<R>,
f: impl FnOnce(
Interned<BitSlice>,
Interned<BitSlice>,
) -> Result<Interned<BitSlice>, HitUndeducedType>,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
let l = l.to_literal_bits()?;
let r = r.to_literal_bits()?;
Ok((|| f(l?, r?))())
}
macro_rules! fixed_ary_op { macro_rules! fixed_ary_op {
( (
pub struct $name:ident<$($T:ident,)* $(#[const] $C:ident: $CTy:ty,)*> pub struct $name:ident<$($T:ident,)* $(#[const] $C:ident: $CTy:ty,)*>
@ -56,7 +93,9 @@ macro_rules! fixed_ary_op {
$($expr_enum_body:tt)+ $($expr_enum_body:tt)+
} }
fn to_literal_bits(&$to_literal_bits_self:ident) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&$to_literal_bits_self:ident,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
$($to_literal_bits_body:tt)+ $($to_literal_bits_body:tt)+
} }
} }
@ -173,7 +212,9 @@ macro_rules! fixed_ary_op {
fn target(&self) -> Option<Interned<Target>> { fn target(&self) -> Option<Interned<Target>> {
($(self.$target_name,)? None::<Interned<Target>>,).0 ($(self.$target_name,)? None::<Interned<Target>>,).0
} }
fn to_literal_bits(&$to_literal_bits_self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&$to_literal_bits_self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
$($to_literal_bits_body)+ $($to_literal_bits_body)+
} }
} }
@ -207,9 +248,8 @@ macro_rules! unary_op {
Valueless::<$T>::from_canonical(arg.valueless()), Valueless::<$T>::from_canonical(arg.valueless()),
).ty, ).ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
arg.to_literal_bits() unary_literal_bits(arg, |v| ops::$Op::$op($T::CanonicalValue::from_bit_slice(&v)).to_bits())
.map(|v| ops::$Op::$op($T::CanonicalValue::from_bit_slice(&v)).to_bits())
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
@ -220,7 +260,9 @@ macro_rules! unary_op {
$($expr_enum_body)+ $($expr_enum_body)+
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -306,18 +348,14 @@ macro_rules! binary_op {
Valueless::<$RhsType>::from_canonical(rhs.valueless()), Valueless::<$RhsType>::from_canonical(rhs.valueless()),
).ty, ).ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
lhs.to_literal_bits() binary_literal_bits(lhs, rhs, |lhs, rhs| {
.ok() ops::$Op::$op(
.zip(rhs.to_literal_bits().ok()) $LhsType::CanonicalValue::from_bit_slice(&lhs),
.map(|(lhs, rhs)| { $RhsType::CanonicalValue::from_bit_slice(&rhs),
ops::$Op::$op( )
$LhsType::CanonicalValue::from_bit_slice(&lhs), .to_bits()
$RhsType::CanonicalValue::from_bit_slice(&rhs), })
)
.to_bits()
})
.ok_or(())
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs.simulate(sim_state)) ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs.simulate(sim_state))
@ -333,7 +371,9 @@ macro_rules! binary_op {
ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()), ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()),
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -539,18 +579,14 @@ macro_rules! dyn_shift_op {
rhs.valueless(), rhs.valueless(),
).ty, ).ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
lhs.to_literal_bits() binary_literal_bits(lhs, rhs, |lhs, rhs| {
.ok() ops::$Op::$op(
.zip(rhs.to_literal_bits().ok()) LhsType::CanonicalValue::from_bit_slice(&lhs),
.map(|(lhs, rhs)| { DynUInt::from_bit_slice(&rhs),
ops::$Op::$op( )
LhsType::CanonicalValue::from_bit_slice(&lhs), .to_bits()
DynUInt::from_bit_slice(&rhs), })
)
.to_bits()
})
.ok_or(())
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs.simulate(sim_state)) ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs.simulate(sim_state))
@ -565,7 +601,9 @@ macro_rules! dyn_shift_op {
ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()), ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()),
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -646,15 +684,14 @@ macro_rules! fixed_shift_op {
ty: <<Valueless<LhsType> as ops::$Op<usize>>::Output as ValuelessTr>::Type = ty: <<Valueless<LhsType> as ops::$Op<usize>>::Output as ValuelessTr>::Type =
ops::$Op::$op(lhs.valueless(), rhs).ty, ops::$Op::$op(lhs.valueless(), rhs).ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
lhs.to_literal_bits() unary_literal_bits(lhs, |lhs| {
.map(|lhs| { ops::$Op::$op(
ops::$Op::$op( LhsType::CanonicalValue::from_bit_slice(&lhs),
LhsType::CanonicalValue::from_bit_slice(&lhs), rhs,
rhs, )
) .to_bits()
.to_bits() })
})
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs) ops::$Op::$op(self.lhs.simulate(sim_state), self.rhs)
@ -669,7 +706,9 @@ macro_rules! fixed_shift_op {
ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()), ConstBoolDispatch::True(v) => ExprEnum::$expr_enum_s(v.intern_sized()),
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -739,18 +778,14 @@ macro_rules! cmp_op {
#[type] #[type]
ty: Output = StaticOrDynIntType::new(), ty: Output = StaticOrDynIntType::new(),
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
lhs.to_literal_bits() binary_literal_bits(lhs, rhs, |lhs, rhs| {
.ok() interned_bit(
.zip(rhs.to_literal_bits().ok()) LhsType::CanonicalValue::from_bit_slice(&lhs)
.map(|(lhs, rhs)| { .value()
interned_bit( .$op(&RhsType::CanonicalValue::from_bit_slice(&rhs).value()),
LhsType::CanonicalValue::from_bit_slice(&lhs) )
.value() })
.$op(&RhsType::CanonicalValue::from_bit_slice(&rhs).value()),
)
})
.ok_or(())
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
self.lhs.simulate(sim_state).$fn(self.rhs.simulate(sim_state)).into_canonical() self.lhs.simulate(sim_state).$fn(self.rhs.simulate(sim_state)).into_canonical()
@ -768,7 +803,9 @@ macro_rules! cmp_op {
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -856,8 +893,8 @@ fixed_ary_op! {
#[type(ty)] #[type(ty)]
pub ty: ToType = ty, pub ty: ToType = ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
value.to_literal_bits().map(|literal_bits| { unary_literal_bits(value, |literal_bits| {
let mut bits = literal_bits.to_bitvec(); let mut bits = literal_bits.to_bitvec();
let fill = FromType::Signed::VALUE let fill = FromType::Signed::VALUE
&& bits.len().checked_sub(1).map(|i| bits[i]).unwrap_or(false); && bits.len().checked_sub(1).map(|i| bits[i]).unwrap_or(false);
@ -901,7 +938,9 @@ fixed_ary_op! {
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -956,8 +995,8 @@ fixed_ary_op! {
#[type] #[type]
ty: DynUIntType = base.valueless().slice(range.clone()).ty, ty: DynUIntType = base.valueless().slice(range.clone()).ty,
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
base.to_literal_bits().map(|base| { unary_literal_bits(base, |base| {
base[range.clone()].intern() base[range.clone()].intern()
}) })
}, },
@ -976,7 +1015,9 @@ fixed_ary_op! {
} }
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1110,8 +1151,8 @@ macro_rules! reduce_bit_op {
#[type] #[type]
ty: Output = StaticOrDynIntType::new(), ty: Output = StaticOrDynIntType::new(),
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
arg.to_literal_bits().map(|$bits| interned_bit($literal_bits_bool)) unary_literal_bits(arg, |$bits| interned_bit($literal_bits_bool))
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
todo!() todo!()
@ -1121,7 +1162,9 @@ macro_rules! reduce_bit_op {
ExprEnum::$name($name::new_unchecked(self.arg).intern_sized()) ExprEnum::$name($name::new_unchecked(self.arg).intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1180,9 +1223,9 @@ fixed_ary_op! {
Field::from_canonical_type(field_ty.expect("field type doesn't match type generic")) Field::from_canonical_type(field_ty.expect("field type doesn't match type generic"))
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
base.to_literal_bits().map(|base| { try_unary_literal_bits(base, |base| {
base[base_ty.field_offsets()[index]..][..field.ty.bit_width()].intern() Ok(base[base_ty.field_offsets()[index]..][..field.ty.bit_width()?].intern())
}) })
}, },
#[target] #[target]
@ -1199,7 +1242,9 @@ fixed_ary_op! {
ExprEnum::FieldAccess(FieldAccess::new_unchecked(self.base, self.name).intern_sized()) ExprEnum::FieldAccess(FieldAccess::new_unchecked(self.base, self.name).intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1239,9 +1284,9 @@ fixed_ary_op! {
base_ty.variants()[variant_index].ty.unwrap_or_else(|| ().canonical_dyn()), base_ty.variants()[variant_index].ty.unwrap_or_else(|| ().canonical_dyn()),
), ),
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
base.to_literal_bits().map(|base| { try_unary_literal_bits(base, |base| {
base[base_ty.discriminant_bit_width()..][..ty.bit_width()].intern() Ok(base[base_ty.discriminant_bit_width()..][..ty.bit_width()?].intern())
}) })
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
@ -1255,7 +1300,9 @@ fixed_ary_op! {
).intern_sized()) ).intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1292,7 +1339,7 @@ fixed_ary_op! {
pub struct CastToBits<> where () { pub struct CastToBits<> where () {
value: Expr<DynCanonicalValue>, value: Expr<DynCanonicalValue>,
#[type] #[type]
ty: DynUIntType = DynUIntType::new(value.ty().bit_width()), ty: DynUIntType = DynUIntType::new(value.ty().bit_width().unwrap_or_else(|e| panic!("can't convert undeduced type to bits: {e}"))),
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
todo!() todo!()
} }
@ -1301,7 +1348,9 @@ fixed_ary_op! {
ExprEnum::CastToBits(self.intern()) ExprEnum::CastToBits(self.intern())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.value.to_literal_bits() self.value.to_literal_bits()
} }
} }
@ -1327,8 +1376,10 @@ fixed_ary_op! {
value: Expr<DynUInt>, value: Expr<DynUInt>,
#[type(ty)] #[type(ty)]
ty: T = { ty: T = {
assert!(ty.is_castable_from_bits(), "can't cast bits to type: {ty:?}"); assert!(ty.is_castable_from_bits().unwrap_or(true), "can't cast bits to type: {ty:?}");
assert_eq!(value.ty().width, ty.bit_width(), "input bit width didn't match target type's bit width"); if let Ok(bit_width) = ty.bit_width() {
assert_eq!(value.ty().width, bit_width, "input bit width didn't match target type's bit width");
}
ty ty
}, },
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
@ -1341,7 +1392,9 @@ fixed_ary_op! {
) )
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.value.to_literal_bits() self.value.to_literal_bits()
} }
} }
@ -1392,7 +1445,9 @@ macro_rules! cast_bit_to_typed_bit {
ExprEnum::$Op($Op::new_unchecked(self.value).intern_sized()) ExprEnum::$Op($Op::new_unchecked(self.value).intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.value.to_literal_bits() self.value.to_literal_bits()
} }
} }
@ -1552,17 +1607,18 @@ fixed_ary_op! {
ty ty
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = (|| {
let mut bits = Some(BitVec::with_capacity(ty.bit_width())); let mut bits = ty.bit_width().map(BitVec::with_capacity);
for element in elements { for element in elements {
let Ok(element_bits) = element.to_literal_bits() else { let element_bits = element.to_literal_bits()?;
bits = None; match (&mut bits, element_bits) {
break; (Ok(_), Err(e)) => bits = Err(e),
}; (Err(_), _) => {}
bits.as_mut().unwrap().extend_from_bitslice(&element_bits); (Ok(bits), Ok(element_bits)) => bits.extend_from_bitslice(&element_bits),
}
} }
bits.map(Intern::intern_owned).ok_or(()) Ok(bits.map(Intern::intern_owned))
}, })(),
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
todo!() todo!()
} }
@ -1571,7 +1627,9 @@ fixed_ary_op! {
ExprEnum::ArrayLiteral(self.canonical().intern_sized()) ExprEnum::ArrayLiteral(self.canonical().intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1603,13 +1661,13 @@ fixed_ary_op! {
base_ty.element().clone() base_ty.element().clone()
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = {
let base_ty = base.ty(); let base_ty = base.ty();
let element_bit_width = base_ty.element().bit_width(); try_unary_literal_bits(base, |base| {
base.to_literal_bits().map(|base| { let element_bit_width = base_ty.element().bit_width()?;
let start = index * element_bit_width; let start = index * element_bit_width;
let end = start + element_bit_width; let end = start + element_bit_width;
base[start..end].intern() Ok(base[start..end].intern())
}) })
}, },
#[target] #[target]
@ -1626,7 +1684,9 @@ fixed_ary_op! {
ExprEnum::ArrayIndex(ArrayIndex::new_unchecked(self.base, self.index).intern_sized()) ExprEnum::ArrayIndex(ArrayIndex::new_unchecked(self.base, self.index).intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1655,18 +1715,32 @@ fixed_ary_op! {
base_ty.element().clone() base_ty.element().clone()
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = (|| {
let base_ty = base.ty(); let base_ty = base.ty();
let element_bit_width = base_ty.element().bit_width(); let base = base.to_literal_bits()?;
base.to_literal_bits().ok().zip(index.to_literal_bits().ok()).and_then(|(base, index)| { let index = index.to_literal_bits()?;
let start = DynUInt::from_bit_slice(&index) let element_bit_width = match base_ty.element().bit_width() {
.uint_value() Ok(v) => v,
.to_usize()? Err(e) => return Ok(Err(e)),
.checked_mul(element_bit_width)?; };
let end = start.checked_add(element_bit_width)?; let base = match base {
Some(base.get(start..end)?.intern()) Ok(v) => v,
}).ok_or(()) Err(e) => return Ok(Err(e)),
}, };
let index = match index {
Ok(v) => v,
Err(e) => return Ok(Err(e)),
};
let start = DynUInt::from_bit_slice(&index)
.uint_value()
.to_usize()
.and_then(|v| v.checked_mul(element_bit_width));
let end = start.and_then(|v| v.checked_add(element_bit_width));
start
.zip(end)
.and_then(|(start, end)| Some(Ok(base.get(start..end)?.intern())))
.ok_or(NotALiteralExpr)
})(),
#[target] #[target]
target: Option<Interned<Target>> = base.target().map(|base| { target: Option<Interned<Target>> = base.target().map(|base| {
Intern::intern_sized(base.join( Intern::intern_sized(base.join(
@ -1683,7 +1757,9 @@ fixed_ary_op! {
) )
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1728,7 +1804,7 @@ impl<
fn expr_index(this: Expr<Self>, index: Expr<IntValue<Idx>>) -> Expr<Self::OutputValue> { fn expr_index(this: Expr<Self>, index: Expr<IntValue<Idx>>) -> Expr<Self::OutputValue> {
let index: Expr<DynUInt> = index.canonical(); let index: Expr<DynUInt> = index.canonical();
if let Ok(index) = index.to_literal_bits() { if let Ok(Ok(index)) = index.to_literal_bits() {
let index = DynUInt::from_bit_slice(&index) let index = DynUInt::from_bit_slice(&index)
.uint_value() .uint_value()
.try_into() .try_into()
@ -1758,7 +1834,7 @@ fixed_ary_op! {
#[type(ty)] #[type(ty)]
ty: BundleTy = { ty: BundleTy = {
assert!( assert!(
ty.is_passive(), ty.is_passive().unwrap_or(true),
concat!( concat!(
"bundle literal must be a passive type (no ", "bundle literal must be a passive type (no ",
"#[flip] fields and all fields must be passive types)", "#[flip] fields and all fields must be passive types)",
@ -1775,17 +1851,18 @@ fixed_ary_op! {
ty ty
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = { literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> = (|| {
let mut bits = Some(BitVec::new()); let mut bits = ty.bit_width().map(BitVec::with_capacity);
for field in fields { for field in fields {
let Ok(v) = field.to_literal_bits() else { let field_bits = field.to_literal_bits()?;
bits = None; match (&mut bits, field_bits) {
break; (Ok(_), Err(e)) => bits = Err(e),
}; (Err(_), _) => {}
bits.as_mut().unwrap().extend_from_bitslice(&v); (Ok(bits), Ok(field_bits)) => bits.extend_from_bitslice(&field_bits),
}
} }
bits.map(Intern::intern_owned).ok_or(()) Ok(bits.map(Intern::intern_owned))
}, })(),
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
todo!() todo!()
} }
@ -1794,7 +1871,9 @@ fixed_ary_op! {
ExprEnum::BundleLiteral(self.canonical().intern_sized()) ExprEnum::BundleLiteral(self.canonical().intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }
@ -1831,7 +1910,7 @@ fixed_ary_op! {
ty ty
}, },
#[cache] #[cache]
literal_bits: Result<Interned<BitSlice>, ()> = literal_bits: Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> =
ty.variant_to_bits(variant_index, variant_value.as_ref()), ty.variant_to_bits(variant_index, variant_value.as_ref()),
fn simulate(&self, sim_state: &mut SimState) -> _ { fn simulate(&self, sim_state: &mut SimState) -> _ {
todo!() todo!()
@ -1841,7 +1920,9 @@ fixed_ary_op! {
ExprEnum::EnumLiteral(self.canonical().intern_sized()) ExprEnum::EnumLiteral(self.canonical().intern_sized())
} }
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> { fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.literal_bits self.literal_bits
} }
} }

View file

@ -27,6 +27,7 @@ use crate::{
DynCanonicalType, DynCanonicalValue, DynCanonicalValueTrait, DynType, DynValue, Type, DynCanonicalType, DynCanonicalValue, DynCanonicalValueTrait, DynType, DynValue, Type,
TypeEnum, Value, ValueEnum, TypeEnum, Value, ValueEnum,
}, },
type_deduction::HitUndeducedType,
util::{ util::{
const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString, const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString,
GenericConstBool, GenericConstBool,
@ -440,6 +441,7 @@ impl TypeState {
TypeEnum::AsyncReset(AsyncResetType {}) => "AsyncReset".into(), TypeEnum::AsyncReset(AsyncResetType {}) => "AsyncReset".into(),
TypeEnum::SyncReset(SyncResetType {}) => "UInt<1>".into(), TypeEnum::SyncReset(SyncResetType {}) => "UInt<1>".into(),
TypeEnum::Reset(ResetType {}) => "Reset".into(), TypeEnum::Reset(ResetType {}) => "Reset".into(),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
} }
} }
} }
@ -468,6 +470,25 @@ impl Default for ModuleState {
struct WrappedError; struct WrappedError;
fn handle_undeduced_type(e: HitUndeducedType) -> ! {
panic!("can't export module with undeduced types: {e}")
}
trait UnwrapUndeduced {
type Output;
fn unwrap_undeduced(self) -> Self::Output;
}
impl<T> UnwrapUndeduced for Result<T, HitUndeducedType> {
type Output = T;
fn unwrap_undeduced(self) -> Self::Output {
match self {
Ok(v) => v,
Err(e) => handle_undeduced_type(e),
}
}
}
trait WrappedFileBackendTrait { trait WrappedFileBackendTrait {
fn write_mem_init_file( fn write_mem_init_file(
&mut self, &mut self,
@ -1012,10 +1033,12 @@ impl<'a> Exporter<'a> {
name, name,
flipped: _, flipped: _,
ty: field_ty, ty: field_ty,
}| FieldType { }| {
name, FieldType {
flipped: false, name,
ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(), flipped: false,
ty: DynUIntType::new(field_ty.bit_width().unwrap_undeduced()).canonical_dyn(),
}
}, },
))); )));
let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty);
@ -1047,7 +1070,7 @@ impl<'a> Exporter<'a> {
let retval = self.module.ns.make_new("_cast_to_bits_expr"); let retval = self.module.ns.make_new("_cast_to_bits_expr");
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}wire {retval}: UInt<{}>", "{extra_indent}wire {retval}: UInt<{}>",
ty.bit_width() ty.bit_width().unwrap_undeduced()
)); ));
let cat_expr = cat_expr.expect("bundle already checked to have fields"); let cat_expr = cat_expr.expect("bundle already checked to have fields");
definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}"));
@ -1066,7 +1089,7 @@ impl<'a> Exporter<'a> {
let retval = self.module.ns.make_new("_cast_enum_to_bits_expr"); let retval = self.module.ns.make_new("_cast_enum_to_bits_expr");
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}wire {retval}: UInt<{}>", "{extra_indent}wire {retval}: UInt<{}>",
ty.bit_width() ty.bit_width().unwrap_undeduced()
)); ));
definitions.add_definition_line(format_args!("{extra_indent}match {value_str}:")); definitions.add_definition_line(format_args!("{extra_indent}match {value_str}:"));
let _match_arms_indent = extra_indent.push(); let _match_arms_indent = extra_indent.push();
@ -1090,7 +1113,7 @@ impl<'a> Exporter<'a> {
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}connect {retval}, pad(cat({variant_bits}, UInt<{}>({variant_index})), {})", "{extra_indent}connect {retval}, pad(cat({variant_bits}, UInt<{}>({variant_index})), {})",
ty.discriminant_bit_width(), ty.discriminant_bit_width(),
ty.bit_width(), ty.bit_width().unwrap_undeduced(),
)); ));
} else { } else {
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
@ -1100,7 +1123,7 @@ impl<'a> Exporter<'a> {
let _match_arm_indent = extra_indent.push(); let _match_arm_indent = extra_indent.push();
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}connect {retval}, UInt<{}>({variant_index})", "{extra_indent}connect {retval}, UInt<{}>({variant_index})",
ty.bit_width(), ty.bit_width().unwrap_undeduced(),
)); ));
} }
} }
@ -1124,7 +1147,7 @@ impl<'a> Exporter<'a> {
extra_indent, extra_indent,
); );
} }
let element_width = ty.element().bit_width(); let element_width = ty.element().bit_width().unwrap_undeduced();
let ident = self.module.ns.make_new("_cast_array_to_bits_expr"); let ident = self.module.ns.make_new("_cast_array_to_bits_expr");
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}wire {ident}: UInt<{element_width}>[{}]", "{extra_indent}wire {ident}: UInt<{element_width}>[{}]",
@ -1150,7 +1173,7 @@ impl<'a> Exporter<'a> {
let retval = self.module.ns.make_new("_cast_to_bits_expr"); let retval = self.module.ns.make_new("_cast_to_bits_expr");
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}wire {retval}: UInt<{}>", "{extra_indent}wire {retval}: UInt<{}>",
ty.bit_width() ty.bit_width().unwrap_undeduced()
)); ));
let cat_expr = cat_expr.expect("array already checked to have elements"); let cat_expr = cat_expr.expect("array already checked to have elements");
definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}")); definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}"));
@ -1178,6 +1201,7 @@ impl<'a> Exporter<'a> {
| TypeEnum::Clock(_) | TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_) | TypeEnum::AsyncReset(_)
| TypeEnum::Reset(_) => format!("asUInt({value_str})"), | TypeEnum::Reset(_) => format!("asUInt({value_str})"),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
} }
} }
fn expr_cast_bits_to_bundle( fn expr_cast_bits_to_bundle(
@ -1199,10 +1223,12 @@ impl<'a> Exporter<'a> {
name, name,
flipped: _, flipped: _,
ty: field_ty, ty: field_ty,
}| FieldType { }| {
name, FieldType {
flipped: false, name,
ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(), flipped: false,
ty: DynUIntType::new(field_ty.bit_width().unwrap_undeduced()).canonical_dyn(),
}
}, },
))); )));
let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty); let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty);
@ -1218,7 +1244,9 @@ impl<'a> Exporter<'a> {
.type_state .type_state
.get_bundle_field(flattened_bundle_ty, field.name); .get_bundle_field(flattened_bundle_ty, field.name);
let field_ident = self.type_state.get_bundle_field(ty, field.name); let field_ident = self.type_state.get_bundle_field(ty, field.name);
if let Some(field_bit_width_minus_one) = field.ty.bit_width().checked_sub(1usize) { if let Some(field_bit_width_minus_one) =
field.ty.bit_width().unwrap_undeduced().checked_sub(1usize)
{
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
"{extra_indent}connect {flattened_ident}.{flattened_field_ident}, bits({value_str}, {}, {field_offset})", "{extra_indent}connect {flattened_ident}.{flattened_field_ident}, bits({value_str}, {}, {field_offset})",
field_offset + field_bit_width_minus_one field_offset + field_bit_width_minus_one
@ -1272,7 +1300,7 @@ impl<'a> Exporter<'a> {
return retval.to_string(); return retval.to_string();
} }
let discriminant_bit_width = ty.discriminant_bit_width(); let discriminant_bit_width = ty.discriminant_bit_width();
let body_bit_width = ty.bit_width() - discriminant_bit_width; let body_bit_width = ty.bit_width().unwrap_undeduced() - discriminant_bit_width;
let body_ident = self.module.ns.make_new("_cast_bits_to_enum_expr_body"); let body_ident = self.module.ns.make_new("_cast_bits_to_enum_expr_body");
let body_value = if body_bit_width != 0 { let body_value = if body_bit_width != 0 {
definitions.add_definition_line(format_args!( definitions.add_definition_line(format_args!(
@ -1328,7 +1356,7 @@ impl<'a> Exporter<'a> {
let retval = self.module.ns.make_new("_cast_bits_to_array_expr"); let retval = self.module.ns.make_new("_cast_bits_to_array_expr");
let array_ty = self.type_state.ty(ty); let array_ty = self.type_state.ty(ty);
definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {array_ty}")); definitions.add_definition_line(format_args!("{extra_indent}wire {retval}: {array_ty}"));
let element_bit_width = ty.element().bit_width(); let element_bit_width = ty.element().bit_width().unwrap_undeduced();
if ty.is_empty() || element_bit_width == 0 { if ty.is_empty() || element_bit_width == 0 {
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}")); definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
return retval.to_string(); return retval.to_string();
@ -1382,6 +1410,7 @@ impl<'a> Exporter<'a> {
TypeEnum::AsyncReset(_) => format!("asAsyncReset({value_str})"), TypeEnum::AsyncReset(_) => format!("asAsyncReset({value_str})"),
TypeEnum::SyncReset(_) => value_str, TypeEnum::SyncReset(_) => value_str,
TypeEnum::Reset(_) => unreachable!("Reset is not bit castable to"), TypeEnum::Reset(_) => unreachable!("Reset is not bit castable to"),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
} }
} }
fn expr_unary<V: Value<Type: Type<Value = V>>>( fn expr_unary<V: Value<Type: Type<Value = V>>>(
@ -1673,13 +1702,13 @@ impl<'a> Exporter<'a> {
) -> Result<(), WrappedError> { ) -> Result<(), WrappedError> {
assert_eq!( assert_eq!(
initial_value.len(), initial_value.len(),
array_type.bit_width(), array_type.bit_width().unwrap_undeduced(),
"literal bits don't match memory array type bit width" "literal bits don't match memory array type bit width"
); );
if initial_value.is_empty() { if initial_value.is_empty() {
return Ok(()); return Ok(());
} }
let element_bit_width = array_type.element().bit_width(); let element_bit_width = array_type.element().bit_width().unwrap_undeduced();
let mut contents = String::new(); let mut contents = String::new();
let hex_or_binary = if element_bit_width % 4 == 0 { let hex_or_binary = if element_bit_width % 4 == 0 {
HexOrBinary::Hex HexOrBinary::Hex
@ -2391,7 +2420,7 @@ fn export_impl(
file_backend: &mut dyn WrappedFileBackendTrait, file_backend: &mut dyn WrappedFileBackendTrait,
top_module: Interned<Module<DynBundle>>, top_module: Interned<Module<DynBundle>>,
) -> Result<(), WrappedError> { ) -> Result<(), WrappedError> {
let top_module = simplify_memories(top_module); let top_module = simplify_memories(top_module).unwrap_undeduced();
let indent_depth = Cell::new(0); let indent_depth = Cell::new(0);
let mut global_ns = Namespace::default(); let mut global_ns = Namespace::default();
let circuit_name = global_ns.get(top_module.name_id()); let circuit_name = global_ns.get(top_module.name_id());

View file

@ -8,6 +8,7 @@ use crate::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
util::{ConstBool, GenericConstBool}, util::{ConstBool, GenericConstBool},
valueless::Valueless, valueless::Valueless,
}; };
@ -339,22 +340,7 @@ impl<
// correct since slice_and_shift ensures we're not trying to slice out of range // correct since slice_and_shift ensures we're not trying to slice out of range
IntValue::with_type(ty, &self.uint_value >> shift) IntValue::with_type(ty, &self.uint_value >> shift)
} }
} pub fn to_bits(&self) -> Interned<BitSlice> {
impl<
Ty: IntTypeTrait<
CanonicalType = DynIntType<<Ty as IntTypeTrait>::Signed>,
CanonicalValue = DynInt<<Ty as IntTypeTrait>::Signed>,
>,
> Value for IntValue<Ty>
{
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
IntValue {
ty: self.ty.canonical(),
uint_value: self.uint_value.clone(),
}
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)] #[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>); struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> { impl<T> Clone for ToBitsMemoize<T> {
@ -387,7 +373,25 @@ impl<
Intern::intern_owned(bits) Intern::intern_owned(bits)
} }
} }
ToBitsMemoize::<Self>(PhantomData).get(this) ToBitsMemoize::<Self>(PhantomData).get(self)
}
}
impl<
Ty: IntTypeTrait<
CanonicalType = DynIntType<<Ty as IntTypeTrait>::Signed>,
CanonicalValue = DynInt<<Ty as IntTypeTrait>::Signed>,
>,
> Value for IntValue<Ty>
{
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
IntValue {
ty: self.ty.canonical(),
uint_value: self.uint_value.clone(),
}
}
fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
Ok(IntValue::to_bits(this))
} }
} }
@ -1111,6 +1115,8 @@ impl DynSIntType {
impl<Signed: GenericConstBool> sealed::Sealed for DynIntType<Signed> {} impl<Signed: GenericConstBool> sealed::Sealed for DynIntType<Signed> {}
impl<Signed: GenericConstBool> Connect<UndeducedType> for DynIntType<Signed> {}
impl<Signed: GenericConstBool> Type for DynIntType<Signed> { impl<Signed: GenericConstBool> Type for DynIntType<Signed> {
type CanonicalType = DynIntType<Signed>; type CanonicalType = DynIntType<Signed>;
type Value = IntValue<DynIntType<Signed>>; type Value = IntValue<DynIntType<Signed>>;
@ -1176,7 +1182,7 @@ impl<Signed: GenericConstBool> CanonicalValue for IntValue<DynIntType<Signed>> {
ValueEnum::UInt(this.clone().as_same_width_uint()) ValueEnum::UInt(this.clone().as_same_width_uint())
} }
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
<Self as Value>::to_bits_impl(this) <Self as Value>::to_bits_impl(this)
} }
} }
@ -1203,7 +1209,9 @@ impl<Signed: GenericConstBool> IntTypeTrait for DynIntType<Signed> {
} }
} }
impl<Signed: GenericConstBool, const WIDTH: usize> StaticOrDynIntType<WIDTH> for DynIntType<Signed> { impl<Signed: GenericConstBool, const WIDTH: usize> StaticOrDynIntType<WIDTH>
for DynIntType<Signed>
{
fn new() -> Self { fn new() -> Self {
DynIntType::new(WIDTH) DynIntType::new(WIDTH)
} }
@ -1233,6 +1241,11 @@ pub type SIntType<const WIDTH: usize> = IntType<ConstBool<true>, WIDTH>;
impl<Signed: GenericConstBool, const WIDTH: usize> sealed::Sealed for IntType<Signed, WIDTH> {} impl<Signed: GenericConstBool, const WIDTH: usize> sealed::Sealed for IntType<Signed, WIDTH> {}
impl<Signed: GenericConstBool, const WIDTH: usize> Connect<UndeducedType>
for IntType<Signed, WIDTH>
{
}
impl<Signed: GenericConstBool, const WIDTH: usize> Type for IntType<Signed, WIDTH> { impl<Signed: GenericConstBool, const WIDTH: usize> Type for IntType<Signed, WIDTH> {
type CanonicalType = DynIntType<Signed>; type CanonicalType = DynIntType<Signed>;
type Value = IntValue<IntType<Signed, WIDTH>>; type Value = IntValue<IntType<Signed, WIDTH>>;

View file

@ -40,6 +40,7 @@ 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 type_deduction;
pub mod util; pub mod util;
pub mod valueless; pub mod valueless;
pub mod wire; pub mod wire;

View file

@ -13,6 +13,7 @@ use crate::{
module::ScopedNameId, module::ScopedNameId,
source_location::SourceLocation, source_location::SourceLocation,
ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value}, ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value},
type_deduction::HitUndeducedType,
util::DebugAsDisplay, util::DebugAsDisplay,
}; };
use bitvec::slice::BitSlice; use bitvec::slice::BitSlice;
@ -492,7 +493,7 @@ impl<T: PortType> MemPort<T> {
mem_element_type: Interned<dyn DynCanonicalType>, mem_element_type: Interned<dyn DynCanonicalType>,
) -> Self { ) -> Self {
assert!( assert!(
mem_element_type.is_storable(), mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type" "memory element type must be a storable type"
); );
Self { Self {
@ -626,7 +627,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
let addr_width = memory_addr_width(array_type.len()); let addr_width = memory_addr_width(array_type.len());
let expected_mem_element_type = array_type.element().canonical_dyn(); let expected_mem_element_type = array_type.element().canonical_dyn();
assert!( assert!(
expected_mem_element_type.is_storable(), expected_mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type" "memory element type must be a storable type"
); );
for (index, port) in ports.iter().enumerate() { for (index, port) in ports.iter().enumerate() {
@ -846,19 +847,21 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
initial_value: Interned<BitSlice>, initial_value: Interned<BitSlice>,
) -> Interned<BitSlice> { ) -> Interned<BitSlice> {
if let Some(depth) = depth { if let Some(depth) = depth {
let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect( if let Ok(mem_element_bit_width) = mem_element_type.bit_width() {
"memory must be small enough that its initializer bit length fits in usize", let expected_len = depth.checked_mul(mem_element_bit_width).expect(
); "memory must be small enough that its initializer bit length fits in usize",
assert_eq!( );
expected_len, assert_eq!(
initial_value.len(), expected_len,
"Mem's initializer bit length doesn't match the expected value", initial_value.len(),
); "Mem's initializer bit length doesn't match the expected value",
);
}
} }
assert!( assert!(
initial_value initial_value
.len() .len()
.checked_rem(mem_element_type.bit_width()) .checked_rem(mem_element_type.bit_width().unwrap_or(1))
.unwrap_or(initial_value.len()) .unwrap_or(initial_value.len())
== 0, == 0,
"Mem's initializer bit length must be a multiple of the element type's bit width", "Mem's initializer bit length must be a multiple of the element type's bit width",
@ -887,8 +890,17 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
let Ok(retval) = initial_value.to_literal_bits() else { let Ok(retval) = initial_value.to_literal_bits() else {
panic!("Mem's initializer must be convertible to literal bits"); panic!("Mem's initializer must be convertible to literal bits");
}; };
let retval = match retval {
Ok(retval) => retval,
Err(HitUndeducedType { .. }) => {
todo!(
"Mem's initializer contains undeduced types, \
you can work around this by using only hdl(static) types"
)
}
};
debug_assert_eq!( debug_assert_eq!(
retval.len(), Ok(retval.len()),
initial_value_ty.bit_width(), initial_value_ty.bit_width(),
"initial value produced wrong literal bits length" "initial value produced wrong literal bits length"
); );
@ -902,7 +914,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
) -> (Self, Rc<RefCell<MemBuilderTarget>>) { ) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
let canonical_mem_element_type = mem_element_type.canonical_dyn(); let canonical_mem_element_type = mem_element_type.canonical_dyn();
assert!( assert!(
canonical_mem_element_type.is_storable(), canonical_mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type" "memory element type must be a storable type"
); );
let target = Rc::new(RefCell::new(MemBuilderTarget { let target = Rc::new(RefCell::new(MemBuilderTarget {
@ -1061,9 +1073,10 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
target.depth, target.depth,
initial_value, initial_value,
)); ));
let element_bit_width = self.mem_element_type.bit_width(); if let Ok(element_bit_width) = self.mem_element_type.bit_width() {
if element_bit_width != 0 { if element_bit_width != 0 {
target.depth = Some(initial_value.len() / element_bit_width); target.depth = Some(initial_value.len() / element_bit_width);
}
} }
} }
pub fn get_read_latency(&self) -> usize { pub fn get_read_latency(&self) -> usize {

View file

@ -20,6 +20,7 @@ use crate::{
CanonicalType, Connect, DynCanonicalType, DynCanonicalValue, DynType, StaticValue, Type, CanonicalType, Connect, DynCanonicalType, DynCanonicalValue, DynType, StaticValue, Type,
TypeEnum, Value, TypeEnum, Value,
}, },
type_deduction::HitUndeducedType,
util::ConstBool, util::ConstBool,
wire::Wire, wire::Wire,
}; };
@ -1414,42 +1415,42 @@ impl TargetState {
} }
} }
} }
fn new(target: Interned<Target>, declared_in_block: usize) -> Self { fn new(target: Interned<Target>, declared_in_block: usize) -> Result<Self, HitUndeducedType> {
Self { Ok(Self {
target, target,
inner: match target.canonical_ty().type_enum() { inner: match target.canonical_ty().type_enum() {
TypeEnum::BundleType(ty) => TargetStateInner::Decomposed { TypeEnum::BundleType(ty) => TargetStateInner::Decomposed {
subtargets: ty subtargets: ty
.fields() .fields()
.iter() .iter()
.map(|&field| { .map(|&field| -> Result<_, HitUndeducedType> {
let path_element = TargetPathElement::intern_sized( let path_element = TargetPathElement::intern_sized(
TargetPathBundleField { name: field.name }.into(), TargetPathBundleField { name: field.name }.into(),
); );
( Ok((
path_element, path_element,
Self::new( Self::new(
Target::intern_sized(target.join(path_element)), Target::intern_sized(target.join(path_element)),
declared_in_block, declared_in_block,
), )?,
) ))
}) })
.collect(), .collect::<Result<_, _>>()?,
}, },
TypeEnum::ArrayType(ty) => TargetStateInner::Decomposed { TypeEnum::ArrayType(ty) => TargetStateInner::Decomposed {
subtargets: (0..ty.len()) subtargets: (0..ty.len())
.map(|index| { .map(|index| -> Result<_, HitUndeducedType> {
let path_element = let path_element =
Intern::intern_sized(TargetPathArrayElement { index }.into()); Intern::intern_sized(TargetPathArrayElement { index }.into());
( Ok((
path_element, path_element,
Self::new( Self::new(
Target::intern_sized(target.join(path_element)), Target::intern_sized(target.join(path_element)),
declared_in_block, declared_in_block,
), )?,
) ))
}) })
.collect(), .collect::<Result<_, _>>()?,
}, },
TypeEnum::EnumType(_) TypeEnum::EnumType(_)
| TypeEnum::UInt(_) | TypeEnum::UInt(_)
@ -1461,8 +1462,9 @@ impl TargetState {
declared_in_block, declared_in_block,
written_in_blocks: RefCell::default(), written_in_blocks: RefCell::default(),
}, },
TypeEnum::Deduce(_) => return Err(HitUndeducedType),
}, },
} })
} }
} }
@ -1524,7 +1526,11 @@ impl AssertValidityState {
self.target_states.get(&target_base).ok_or(()) self.target_states.get(&target_base).ok_or(())
} }
#[track_caller] #[track_caller]
fn insert_new_base(&mut self, target_base: Interned<TargetBase>, declared_in_block: usize) { fn insert_new_base(
&mut self,
target_base: Interned<TargetBase>,
declared_in_block: usize,
) -> Result<(), HitUndeducedType> {
match self.target_states.entry(target_base) { match self.target_states.entry(target_base) {
Entry::Occupied(_) => panic!( Entry::Occupied(_) => panic!(
"at {}: duplicate declaration: {target_base}", "at {}: duplicate declaration: {target_base}",
@ -1534,9 +1540,10 @@ impl AssertValidityState {
entry.insert(TargetState::new( entry.insert(TargetState::new(
Target::intern_sized(target_base.into()), Target::intern_sized(target_base.into()),
declared_in_block, declared_in_block,
)); )?);
} }
} }
Ok(())
} }
fn set_connect_target_written( fn set_connect_target_written(
target_state: &TargetState, target_state: &TargetState,
@ -1625,29 +1632,30 @@ impl AssertValidityState {
&mut self, &mut self,
parent_block: usize, parent_block: usize,
sub_blocks: impl Clone + IntoIterator<Item = usize>, sub_blocks: impl Clone + IntoIterator<Item = usize>,
) { ) -> Result<(), HitUndeducedType> {
for i in sub_blocks.clone() { for i in sub_blocks.clone() {
self.assert_subtree_validity(i); self.assert_subtree_validity(i)?;
} }
for i in self.target_states.values() { for i in self.target_states.values() {
i.merge_conditional_sub_blocks_into_block(parent_block, sub_blocks.clone()); i.merge_conditional_sub_blocks_into_block(parent_block, sub_blocks.clone());
} }
Ok(())
} }
#[track_caller] #[track_caller]
fn assert_subtree_validity(&mut self, block: usize) { fn assert_subtree_validity(&mut self, block: usize) -> Result<(), HitUndeducedType> {
let module = self.module; let module = self.module;
if block == 0 { if block == 0 {
for module_io in &*module.module_io { for module_io in &*module.module_io {
self.insert_new_base( self.insert_new_base(
TargetBase::intern_sized(module_io.module_io.clone().into()), TargetBase::intern_sized(module_io.module_io.clone().into()),
block, block,
); )?;
} }
} }
let Block { memories, stmts } = self.blocks[block]; let Block { memories, stmts } = self.blocks[block];
for m in memories { for m in memories {
for port in m.ports() { for port in m.ports() {
self.insert_new_base(TargetBase::intern_sized((*port).into()), block); self.insert_new_base(TargetBase::intern_sized((*port).into()), block)?;
} }
} }
for stmt in stmts { for stmt in stmts {
@ -1662,7 +1670,7 @@ impl AssertValidityState {
} }
Stmt::If(if_stmt) => { Stmt::If(if_stmt) => {
let sub_blocks = if_stmt.blocks.map(|block| self.make_block_index(block)); let sub_blocks = if_stmt.blocks.map(|block| self.make_block_index(block));
self.process_conditional_sub_blocks(block, sub_blocks) self.process_conditional_sub_blocks(block, sub_blocks)?
} }
Stmt::Match(match_stmt) => { Stmt::Match(match_stmt) => {
let sub_blocks = Vec::from_iter( let sub_blocks = Vec::from_iter(
@ -1671,22 +1679,23 @@ impl AssertValidityState {
.into_iter() .into_iter()
.map(|block| self.make_block_index(block)), .map(|block| self.make_block_index(block)),
); );
self.process_conditional_sub_blocks(block, sub_blocks.iter().copied()) self.process_conditional_sub_blocks(block, sub_blocks.iter().copied())?
} }
Stmt::Declaration(StmtDeclaration::Wire(StmtWire { Stmt::Declaration(StmtDeclaration::Wire(StmtWire {
annotations: _, annotations: _,
wire, wire,
})) => self.insert_new_base(TargetBase::intern_sized(wire.into()), block), })) => self.insert_new_base(TargetBase::intern_sized(wire.into()), block)?,
Stmt::Declaration(StmtDeclaration::Reg(StmtReg { Stmt::Declaration(StmtDeclaration::Reg(StmtReg {
annotations: _, annotations: _,
reg, reg,
})) => self.insert_new_base(TargetBase::intern_sized(reg.into()), block), })) => self.insert_new_base(TargetBase::intern_sized(reg.into()), block)?,
Stmt::Declaration(StmtDeclaration::Instance(StmtInstance { Stmt::Declaration(StmtDeclaration::Instance(StmtInstance {
annotations: _, annotations: _,
instance, instance,
})) => self.insert_new_base(TargetBase::intern_sized(instance.into()), block), })) => self.insert_new_base(TargetBase::intern_sized(instance.into()), block)?,
} }
} }
Ok(())
} }
#[track_caller] #[track_caller]
fn assert_validity(&mut self) { fn assert_validity(&mut self) {
@ -1698,10 +1707,11 @@ impl AssertValidityState {
ModuleBody::Normal(NormalModuleBody { body }) => { ModuleBody::Normal(NormalModuleBody { body }) => {
let body = self.make_block_index(body); let body = self.make_block_index(body);
assert_eq!(body, 0); assert_eq!(body, 0);
self.assert_subtree_validity(body); if let Ok(()) = self.assert_subtree_validity(body) {
for (base, state) in &self.target_states { for (base, state) in &self.target_states {
if base.must_connect_to() { if base.must_connect_to() {
state.assert_written(); state.assert_written();
}
} }
} }
} }
@ -2367,7 +2377,7 @@ where
match rhs.flow() { match rhs.flow() {
Flow::Source | Flow::Duplex => {} Flow::Source | Flow::Duplex => {}
Flow::Sink => assert!( Flow::Sink => assert!(
rhs.ty().is_passive(), rhs.ty().is_passive().unwrap_or(true),
"can't connect from sink with non-passive type" "can't connect from sink with non-passive type"
), ),
} }

View file

@ -14,6 +14,7 @@ use crate::{
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum}, ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum},
type_deduction::HitUndeducedType,
wire::Wire, wire::Wire,
}; };
use core::fmt; use core::fmt;
@ -22,6 +23,7 @@ use hashbrown::HashMap;
#[derive(Debug)] #[derive(Debug)]
pub enum SimplifyEnumsError { pub enum SimplifyEnumsError {
EnumIsNotCastableFromBits { enum_type: DynEnumType }, EnumIsNotCastableFromBits { enum_type: DynEnumType },
HitUndeducedType(HitUndeducedType),
} }
impl fmt::Display for SimplifyEnumsError { impl fmt::Display for SimplifyEnumsError {
@ -31,12 +33,19 @@ impl fmt::Display for SimplifyEnumsError {
f, f,
"simplify_enums failed: enum type is not castable from bits: {enum_type:?}" "simplify_enums failed: enum type is not castable from bits: {enum_type:?}"
), ),
SimplifyEnumsError::HitUndeducedType(e) => e.fmt(f),
} }
} }
} }
impl std::error::Error for SimplifyEnumsError {} impl std::error::Error for SimplifyEnumsError {}
impl From<HitUndeducedType> for SimplifyEnumsError {
fn from(value: HitUndeducedType) -> Self {
Self::HitUndeducedType(value)
}
}
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)] #[derive(Value, Clone, Eq, PartialEq, Hash, Debug)]
struct TagAndBody<T> { struct TagAndBody<T> {
tag: T, tag: T,
@ -69,7 +78,7 @@ impl State {
if let Some(retval) = self.enum_types.get(&enum_type) { if let Some(retval) = self.enum_types.get(&enum_type) {
return Ok(retval.clone()); return Ok(retval.clone());
} }
if !enum_type.is_castable_from_bits() { if !enum_type.is_castable_from_bits()? {
return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type }); return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
} }
let has_body = enum_type let has_body = enum_type
@ -86,7 +95,7 @@ impl State {
}, },
))), ))),
body: DynUIntType::new( body: DynUIntType::new(
enum_type.bit_width() - enum_type.discriminant_bit_width(), enum_type.bit_width()? - enum_type.discriminant_bit_width(),
), ),
}) })
} }
@ -95,12 +104,12 @@ impl State {
EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> { EnumTypeState::TagUIntAndBody(TagAndBodyType::<DynUInt> {
tag: DynUIntType::new(enum_type.discriminant_bit_width()), tag: DynUIntType::new(enum_type.discriminant_bit_width()),
body: DynUIntType::new( body: DynUIntType::new(
enum_type.bit_width() - enum_type.discriminant_bit_width(), enum_type.bit_width()? - enum_type.discriminant_bit_width(),
), ),
}) })
} }
(SimplifyEnumsKind::ReplaceWithUInt, _) => { (SimplifyEnumsKind::ReplaceWithUInt, _) => {
EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width())) EnumTypeState::UInt(DynUIntType::new(enum_type.bit_width()?))
} }
}; };
self.enum_types.insert(enum_type, retval.clone()); self.enum_types.insert(enum_type, retval.clone());
@ -108,11 +117,14 @@ impl State {
} }
} }
fn value_to_uint<T: Value>(value: Option<&T>, target_ty: DynUIntType) -> DynUInt { fn value_to_uint<T: Value>(
value: Option<&T>,
target_ty: DynUIntType,
) -> Result<DynUInt, HitUndeducedType> {
let Some(value) = value else { let Some(value) = value else {
return DynUInt::with_type(target_ty, 0u8); return Ok(DynUInt::with_type(target_ty, 0u8));
}; };
DynUInt::from_bit_slice(&value.to_bits()) Ok(DynUInt::from_bit_slice(&value.to_bits()?))
} }
fn connect_port( fn connect_port(
@ -120,7 +132,7 @@ fn connect_port(
lhs: Expr<DynCanonicalValue>, lhs: Expr<DynCanonicalValue>,
rhs: Expr<DynCanonicalValue>, rhs: Expr<DynCanonicalValue>,
source_location: SourceLocation, source_location: SourceLocation,
) { ) -> Result<(), HitUndeducedType> {
println!("connect_port: lhs={lhs:?} rhs={rhs:?}"); println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
if lhs.canonical_type() == rhs.canonical_type() { if lhs.canonical_type() == rhs.canonical_type() {
stmts.push( stmts.push(
@ -131,24 +143,25 @@ fn connect_port(
}) })
.into(), .into(),
); );
return; return Ok(());
} }
match ( match (
lhs.canonical_type().type_enum(), lhs.canonical_type().type_enum(),
rhs.canonical_type().type_enum(), rhs.canonical_type().type_enum(),
) { ) {
(TypeEnum::Deduce(_), _) | (_, TypeEnum::Deduce(_)) => return Err(HitUndeducedType),
(TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => { (TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
let lhs = lhs.with_type::<DynBundle>(); let lhs = lhs.with_type::<DynBundle>();
for field in lhs_type.fields() { for field in lhs_type.fields() {
assert!(!field.flipped); assert!(!field.flipped);
connect_port(stmts, lhs.field(&field.name), rhs, source_location); connect_port(stmts, lhs.field(&field.name), rhs, source_location)?;
} }
} }
(TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => { (TypeEnum::UInt(_), TypeEnum::BundleType(rhs_type)) => {
let rhs = rhs.with_type::<DynBundle>(); let rhs = rhs.with_type::<DynBundle>();
for field in rhs_type.fields() { for field in rhs_type.fields() {
assert!(!field.flipped); assert!(!field.flipped);
connect_port(stmts, lhs, rhs.field(&field.name), source_location); connect_port(stmts, lhs, rhs.field(&field.name), source_location)?;
} }
} }
(TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => { (TypeEnum::BundleType(lhs_type), TypeEnum::BundleType(_)) => {
@ -160,14 +173,14 @@ fn connect_port(
} else { } else {
(lhs.field(&field.name), rhs.field(&field.name)) (lhs.field(&field.name), rhs.field(&field.name))
}; };
connect_port(stmts, lhs_field, rhs_field, source_location); connect_port(stmts, lhs_field, rhs_field, source_location)?;
} }
} }
(TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => { (TypeEnum::ArrayType(lhs_type), TypeEnum::ArrayType(_)) => {
let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>(); let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>();
let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>(); let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>();
for index in 0..lhs_type.len() { for index in 0..lhs_type.len() {
connect_port(stmts, lhs[index], rhs[index], source_location); connect_port(stmts, lhs[index], rhs[index], source_location)?;
} }
} }
(TypeEnum::BundleType(_), _) (TypeEnum::BundleType(_), _)
@ -184,6 +197,7 @@ fn connect_port(
rhs.canonical_type().type_enum(), rhs.canonical_type().type_enum(),
), ),
} }
Ok(())
} }
impl Folder for State { impl Folder for State {
@ -240,7 +254,7 @@ impl Folder for State {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(), Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type( None => DynUInt::with_type(
DynUIntType::new( DynUIntType::new(
op.ty().bit_width() - op.ty().discriminant_bit_width(), op.ty().bit_width()? - op.ty().discriminant_bit_width(),
), ),
0u8, 0u8,
) )
@ -268,7 +282,7 @@ impl Folder for State {
.fold(self)? .fold(self)?
.to_expr() .to_expr()
.with_type::<TagAndBody<DynCanonicalValue>>() .with_type::<TagAndBody<DynCanonicalValue>>()
.body[..op.ty().bit_width()] .body[..op.ty().bit_width()?]
.cast_bits_to::<DynCanonicalValue>(op.ty()) .cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum(), .expr_enum(),
None => ().to_expr().expr_enum(), None => ().to_expr().expr_enum(),
@ -284,7 +298,7 @@ impl Folder for State {
.with_type::<DynUInt>(); .with_type::<DynUInt>();
dbg!(base_int); dbg!(base_int);
let base_ty = op.base().ty(); let base_ty = op.base().ty();
let ty_bit_width = op.ty().bit_width(); let ty_bit_width = op.ty().bit_width()?;
base_int[base_ty.discriminant_bit_width()..][..ty_bit_width] base_int[base_ty.discriminant_bit_width()..][..ty_bit_width]
.cast_bits_to::<DynCanonicalValue>(op.ty()) .cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum() .expr_enum()
@ -428,7 +442,7 @@ impl Folder for State {
new_port.to_expr().to_canonical_dyn(), new_port.to_expr().to_canonical_dyn(),
wire.to_expr().to_canonical_dyn(), wire.to_expr().to_canonical_dyn(),
port.source_location(), port.source_location(),
); )?;
self.replacement_mem_ports self.replacement_mem_ports
.insert(port, wire.to_dyn_canonical_wire().intern_sized()); .insert(port, wire.to_dyn_canonical_wire().intern_sized());
} }
@ -562,6 +576,7 @@ impl Folder for State {
| TypeEnum::AsyncReset(_) | TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_) | TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => type_enum.default_fold(self), | TypeEnum::Reset(_) => type_enum.default_fold(self),
TypeEnum::Deduce(_) => Err(HitUndeducedType.into()),
} }
} }
@ -575,7 +590,7 @@ impl Folder for State {
}) => ValueEnum::Bundle( }) => ValueEnum::Bundle(
TagAndBody { TagAndBody {
tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None), tag: DynEnum::new_by_index(tag_ty, enum_value.variant_index(), None),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty), body: value_to_uint(enum_value.variant_value().as_ref(), body_ty)?,
} }
.to_canonical(), .to_canonical(),
), ),
@ -585,12 +600,12 @@ impl Folder for State {
}) => ValueEnum::Bundle( }) => ValueEnum::Bundle(
TagAndBody { TagAndBody {
tag: DynUInt::with_type(tag_ty, enum_value.variant_index()), tag: DynUInt::with_type(tag_ty, enum_value.variant_index()),
body: value_to_uint(enum_value.variant_value().as_ref(), body_ty), body: value_to_uint(enum_value.variant_value().as_ref(), body_ty)?,
} }
.to_canonical(), .to_canonical(),
), ),
EnumTypeState::UInt(target_ty) => { EnumTypeState::UInt(target_ty) => {
ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty)) ValueEnum::UInt(value_to_uint(Some(&enum_value), target_ty)?)
} }
EnumTypeState::Unchanged => ValueEnum::Enum(enum_value), EnumTypeState::Unchanged => ValueEnum::Enum(enum_value),
}) })

View file

@ -14,13 +14,13 @@ use crate::{
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalValue, DynType, Type, TypeEnum}, ty::{DynCanonicalValue, DynType, Type, TypeEnum},
type_deduction::HitUndeducedType,
util::MakeMutSlice, util::MakeMutSlice,
wire::Wire, wire::Wire,
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{ use std::{
convert::Infallible,
fmt::Write, fmt::Write,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
rc::Rc, rc::Rc,
@ -83,17 +83,17 @@ impl MemSplit {
MemSplit::Array { elements: _ } => self, MemSplit::Array { elements: _ } => self,
} }
} }
fn new(element_type: TypeEnum) -> Self { fn new(element_type: TypeEnum) -> Result<Self, HitUndeducedType> {
match element_type { Ok(match element_type {
TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle { TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle {
fields: bundle_ty fields: bundle_ty
.fields() .fields()
.into_iter() .into_iter()
.map(|field| Self::new(field.ty.type_enum()).mark_changed_element_type()) .map(|field| Ok(Self::new(field.ty.type_enum())?.mark_changed_element_type()))
.collect(), .collect::<Result<_, _>>()?,
}, },
TypeEnum::ArrayType(ty) => { TypeEnum::ArrayType(ty) => {
let element = MemSplit::new(ty.element().type_enum()); let element = MemSplit::new(ty.element().type_enum())?;
if let Self::Single { if let Self::Single {
output_mem: _, output_mem: _,
element_type, element_type,
@ -157,14 +157,15 @@ impl MemSplit {
}, },
TypeEnum::EnumType(ty) => Self::Single { TypeEnum::EnumType(ty) => Self::Single {
output_mem: None, output_mem: None,
element_type: SingleType::UInt(DynUIntType::new(ty.bit_width())), element_type: SingleType::UInt(DynUIntType::new(ty.bit_width()?)),
unchanged_element_type: false, unchanged_element_type: false,
}, },
TypeEnum::Clock(_) TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_) | TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_) | TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"), | TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
} TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
} }
} }
@ -271,7 +272,7 @@ struct SplitMemState<'a, 'b> {
} }
impl SplitMemState<'_, '_> { impl SplitMemState<'_, '_> {
fn split_mem(self) { fn split_mem(self) -> Result<(), HitUndeducedType> {
let outer_mem_name_path_len = self.mem_name_path.len(); let outer_mem_name_path_len = self.mem_name_path.len();
match self.split { match self.split {
MemSplit::Bundle { fields } => { MemSplit::Bundle { fields } => {
@ -287,7 +288,7 @@ impl SplitMemState<'_, '_> {
self.mem_name_path.truncate(outer_mem_name_path_len); self.mem_name_path.truncate(outer_mem_name_path_len);
self.mem_name_path.push('_'); self.mem_name_path.push('_');
self.mem_name_path.push_str(&field.name); self.mem_name_path.push_str(&field.name);
let field_ty_bit_width = field.ty.bit_width(); let field_ty_bit_width = field.ty.bit_width()?;
self.split_state_stack.push_map( self.split_state_stack.push_map(
|e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name), |e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name),
|initial_value_element| { |initial_value_element| {
@ -305,7 +306,7 @@ impl SplitMemState<'_, '_> {
split_state_stack: self.split_state_stack, split_state_stack: self.split_state_stack,
mem_state: self.mem_state, mem_state: self.mem_state,
} }
.split_mem(); .split_mem()?;
self.split_state_stack.pop(); self.split_state_stack.pop();
} }
} }
@ -321,7 +322,7 @@ impl SplitMemState<'_, '_> {
*single_type, *single_type,
self.mem_name_path, self.mem_name_path,
self.split_state_stack.top(), self.split_state_stack.top(),
); )?;
for (port, wire) in new_mem for (port, wire) in new_mem
.ports() .ports()
.into_iter() .into_iter()
@ -356,7 +357,7 @@ impl SplitMemState<'_, '_> {
unreachable!(); unreachable!();
}; };
let element_type = array_type.element().type_enum(); let element_type = array_type.element().type_enum();
let element_bit_width = array_type.element().bit_width(); let element_bit_width = array_type.element().bit_width()?;
for (index, split) in elements.make_mut_slice().iter_mut().enumerate() { for (index, split) in elements.make_mut_slice().iter_mut().enumerate() {
self.mem_name_path.truncate(outer_mem_name_path_len); self.mem_name_path.truncate(outer_mem_name_path_len);
write!(self.mem_name_path, "_{index}").unwrap(); write!(self.mem_name_path, "_{index}").unwrap();
@ -379,11 +380,12 @@ impl SplitMemState<'_, '_> {
split_state_stack: self.split_state_stack, split_state_stack: self.split_state_stack,
mem_state: self.mem_state, mem_state: self.mem_state,
} }
.split_mem(); .split_mem()?;
self.split_state_stack.pop(); self.split_state_stack.pop();
} }
} }
} }
Ok(())
} }
} }
@ -465,7 +467,7 @@ impl ModuleState {
port_rdata: Option<Expr<DynCanonicalValue>>, port_rdata: Option<Expr<DynCanonicalValue>>,
port_wdata: Option<Expr<DynCanonicalValue>>, port_wdata: Option<Expr<DynCanonicalValue>>,
port_wmask: Option<Expr<DynCanonicalValue>>, port_wmask: Option<Expr<DynCanonicalValue>>,
) { ) -> Result<(), HitUndeducedType> {
let mut input_array_types = vec![]; let mut input_array_types = vec![];
let connect_read = |output_stmts: &mut Vec<Stmt>, let connect_read = |output_stmts: &mut Vec<Stmt>,
wire_read: Expr<DynCanonicalValue>, wire_read: Expr<DynCanonicalValue>,
@ -599,9 +601,11 @@ impl ModuleState {
| TypeEnum::AsyncReset(_) | TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_) | TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"), | TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
TypeEnum::Deduce(_) => return Err(HitUndeducedType),
} }
break; break;
} }
Ok(())
} }
fn create_split_mem( fn create_split_mem(
&mut self, &mut self,
@ -611,7 +615,7 @@ impl ModuleState {
single_type: SingleType, single_type: SingleType,
mem_name_path: &str, mem_name_path: &str,
split_state: &SplitState<'_>, split_state: &SplitState<'_>,
) -> Mem<[DynCanonicalValue]> { ) -> Result<Mem<[DynCanonicalValue]>, HitUndeducedType> {
let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!( let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
"{}{mem_name_path}", "{}{mem_name_path}",
input_mem.scoped_name().1 .0 input_mem.scoped_name().1 .0
@ -625,8 +629,9 @@ impl ModuleState {
}; };
let output_array_type = let output_array_type =
ArrayType::new_slice(output_element_type, input_mem.array_type().len()); ArrayType::new_slice(output_element_type, input_mem.array_type().len());
let output_array_bit_width = output_array_type.bit_width()?;
let initial_value = split_state.initial_value.as_ref().map(|initial_value| { let initial_value = split_state.initial_value.as_ref().map(|initial_value| {
let mut bits = BitVec::with_capacity(output_array_type.bit_width()); let mut bits = BitVec::with_capacity(output_array_bit_width);
for element in initial_value.iter() { for element in initial_value.iter() {
bits.extend_from_bitslice(element); bits.extend_from_bitslice(element);
} }
@ -696,18 +701,18 @@ impl ModuleState {
port_rdata, port_rdata,
port_wdata, port_wdata,
port_wmask, port_wmask,
); )?;
} }
output_mem Ok(output_mem)
} }
fn process_mem( fn process_mem(
&mut self, &mut self,
input_mem: Mem<[DynCanonicalValue]>, input_mem: Mem<[DynCanonicalValue]>,
output_mems: &mut Vec<Mem<[DynCanonicalValue]>>, output_mems: &mut Vec<Mem<[DynCanonicalValue]>>,
output_stmts: &mut Vec<Stmt>, output_stmts: &mut Vec<Stmt>,
) { ) -> Result<(), HitUndeducedType> {
let element_type = input_mem.array_type().element().type_enum(); let element_type = input_mem.array_type().element().type_enum();
let mut split = MemSplit::new(element_type); let mut split = MemSplit::new(element_type)?;
let mem_state = match split { let mem_state = match split {
MemSplit::Single { MemSplit::Single {
ref mut output_mem, ref mut output_mem,
@ -805,11 +810,12 @@ impl ModuleState {
), ),
mem_state: &mem_state, mem_state: &mem_state,
} }
.split_mem(); .split_mem()?;
mem_state mem_state
} }
}; };
self.memories.insert(input_mem.scoped_name(), mem_state); self.memories.insert(input_mem.scoped_name(), mem_state);
Ok(())
} }
} }
@ -858,7 +864,7 @@ impl DerefMut for PushedState<'_> {
} }
impl Folder for State { impl Folder for State {
type Error = Infallible; type Error = HitUndeducedType;
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
where where
@ -902,13 +908,11 @@ impl Folder for State {
let mut output_stmts = vec![]; let mut output_stmts = vec![];
let module_state = self.module_state(); let module_state = self.module_state();
for input_mem in input_mems { for input_mem in input_mems {
module_state.process_mem(input_mem, &mut output_mems, &mut output_stmts); module_state.process_mem(input_mem, &mut output_mems, &mut output_stmts)?;
}
for stmt in input_stmts {
output_stmts.push(stmt.fold(self)?);
} }
output_stmts.extend(
input_stmts
.into_iter()
.map(|stmt| stmt.fold(self).unwrap_or_else(|v| match v {})),
);
Ok(Block { Ok(Block {
memories: Intern::intern_owned(output_mems), memories: Intern::intern_owned(output_mems),
stmts: Intern::intern_owned(output_stmts), stmts: Intern::intern_owned(output_stmts),
@ -933,8 +937,8 @@ impl Folder for State {
} }
} }
pub fn simplify_memories(module: Interned<Module<DynBundle>>) -> Interned<Module<DynBundle>> { pub fn simplify_memories(
module module: Interned<Module<DynBundle>>,
.fold(&mut State::default()) ) -> Result<Interned<Module<DynBundle>>, HitUndeducedType> {
.unwrap_or_else(|v| match v {}) module.fold(&mut State::default())
} }

View file

@ -12,8 +12,8 @@ use crate::{
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr, TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
}, },
int::{ int::{
DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, IntType, DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, IntType, IntTypeTrait,
IntTypeTrait, StaticOrDynIntType,
}, },
intern::{Intern, Interned}, intern::{Intern, Interned},
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite}, memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
@ -27,6 +27,7 @@ use crate::{
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType}, reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
source_location::SourceLocation, source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum}, ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum},
type_deduction::UndeducedType,
util::{ConstBool, GenericConstBool}, util::{ConstBool, GenericConstBool},
wire::Wire, wire::Wire,
}; };

View file

@ -106,7 +106,10 @@ impl<T: Type> Reg<T> {
clock_domain: Expr<ClockDomain>, clock_domain: Expr<ClockDomain>,
init: Option<Expr<T::Value>>, init: Option<Expr<T::Value>>,
) -> Self { ) -> Self {
assert!(ty.is_storable(), "register type must be a storable type"); assert!(
ty.is_storable().unwrap_or(true),
"register type must be a storable type"
);
if let Some(init) = init { if let Some(init) = init {
assert_eq!(ty, init.ty(), "register's type must match init type"); assert_eq!(ty, init.ty(), "register's type must match init type");
} }

View file

@ -9,6 +9,7 @@ use crate::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum, DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
}, },
type_deduction::{HitUndeducedType, UndeducedType},
util::interned_bit, util::interned_bit,
}; };
use bitvec::slice::BitSlice; use bitvec::slice::BitSlice;
@ -24,6 +25,8 @@ impl AsyncResetType {
} }
} }
impl Connect<UndeducedType> for AsyncResetType {}
impl Type for AsyncResetType { impl Type for AsyncResetType {
type Value = AsyncReset; type Value = AsyncReset;
type CanonicalType = AsyncResetType; type CanonicalType = AsyncResetType;
@ -91,8 +94,8 @@ impl Value for AsyncReset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self *self
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }
@ -100,8 +103,8 @@ impl CanonicalValue for AsyncReset {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::AsyncReset(*this) ValueEnum::AsyncReset(*this)
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }
@ -114,6 +117,8 @@ impl SyncResetType {
} }
} }
impl Connect<UndeducedType> for SyncResetType {}
impl Type for SyncResetType { impl Type for SyncResetType {
type CanonicalType = SyncResetType; type CanonicalType = SyncResetType;
type Value = SyncReset; type Value = SyncReset;
@ -181,8 +186,8 @@ impl Value for SyncReset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self *self
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }
@ -190,8 +195,8 @@ impl CanonicalValue for SyncReset {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::SyncReset(*this) ValueEnum::SyncReset(*this)
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
interned_bit(this.0) Ok(interned_bit(this.0))
} }
} }
@ -204,6 +209,8 @@ impl ResetType {
} }
} }
impl Connect<UndeducedType> for ResetType {}
impl Type for ResetType { impl Type for ResetType {
type Value = Reset; type Value = Reset;
type CanonicalType = ResetType; type CanonicalType = ResetType;
@ -271,7 +278,7 @@ impl Value for Reset {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self *self
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
match *this {} match *this {}
} }
} }
@ -280,7 +287,7 @@ impl CanonicalValue for Reset {
fn value_enum_impl(this: &Self) -> ValueEnum { fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Reset(*this) ValueEnum::Reset(*this)
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
match *this {} match *this {}
} }
} }

View file

@ -14,6 +14,7 @@ use crate::{
}, },
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType}, reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
source_location::SourceLocation, source_location::SourceLocation,
type_deduction::{Deduce, HitUndeducedType, UndeducedType},
util::{iter_eq_by, GenericConstBool}, util::{iter_eq_by, GenericConstBool},
valueless::Valueless, valueless::Valueless,
}; };
@ -46,6 +47,7 @@ pub enum TypeEnum {
AsyncReset(AsyncResetType), AsyncReset(AsyncResetType),
SyncReset(SyncResetType), SyncReset(SyncResetType),
Reset(ResetType), Reset(ResetType),
Deduce(UndeducedType),
} }
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
@ -60,6 +62,7 @@ pub enum CanonicalTypeKind {
SyncReset, SyncReset,
Reset, Reset,
DynCanonicalType, DynCanonicalType,
Deduce,
} }
impl CanonicalTypeKind { impl CanonicalTypeKind {
@ -75,6 +78,7 @@ impl CanonicalTypeKind {
CanonicalTypeKind::SyncReset => "Expr<SyncReset>", CanonicalTypeKind::SyncReset => "Expr<SyncReset>",
CanonicalTypeKind::Reset => "Expr<Reset>", CanonicalTypeKind::Reset => "Expr<Reset>",
CanonicalTypeKind::DynCanonicalType => "Expr<DynCanonicalValue>", CanonicalTypeKind::DynCanonicalType => "Expr<DynCanonicalValue>",
CanonicalTypeKind::Deduce => "Expr<Deduce>",
} }
} }
} }
@ -93,57 +97,61 @@ pub enum ValueEnum {
} }
impl TypeEnum { impl TypeEnum {
pub fn is_passive(self) -> bool { pub fn is_passive(self) -> Result<bool, HitUndeducedType> {
match self { Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_passive(ty), TypeEnum::BundleType(ty) => DynBundleType::is_passive(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_passive(ty), TypeEnum::EnumType(ty) => DynEnumType::is_passive(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_passive(), TypeEnum::ArrayType(ty) => ty.element().is_passive()?,
TypeEnum::UInt(_) => true, TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true, TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => true, TypeEnum::Clock(_) => true,
TypeEnum::AsyncReset(_) => true, TypeEnum::AsyncReset(_) => true,
TypeEnum::SyncReset(_) => true, TypeEnum::SyncReset(_) => true,
TypeEnum::Reset(_) => true, TypeEnum::Reset(_) => true,
} TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
} }
pub fn is_storable(self) -> bool { pub fn is_storable(self) -> Result<bool, HitUndeducedType> {
match self { Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_storable(ty), TypeEnum::BundleType(ty) => DynBundleType::is_storable(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_storable(ty), TypeEnum::EnumType(ty) => DynEnumType::is_storable(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_storable(), TypeEnum::ArrayType(ty) => ty.element().is_storable()?,
TypeEnum::UInt(_) => true, TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true, TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => false, TypeEnum::Clock(_) => false,
TypeEnum::AsyncReset(_) => false, TypeEnum::AsyncReset(_) => false,
TypeEnum::SyncReset(_) => false, TypeEnum::SyncReset(_) => false,
TypeEnum::Reset(_) => false, TypeEnum::Reset(_) => false,
} TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
} }
pub fn is_castable_from_bits(self) -> bool { pub fn is_castable_from_bits(self) -> Result<bool, HitUndeducedType> {
match self { Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_castable_from_bits(ty), TypeEnum::BundleType(ty) => DynBundleType::is_castable_from_bits(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_castable_from_bits(ty), TypeEnum::EnumType(ty) => DynEnumType::is_castable_from_bits(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_castable_from_bits(), TypeEnum::ArrayType(ty) => ty.element().is_castable_from_bits()?,
TypeEnum::UInt(_) => true, TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true, TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => true, TypeEnum::Clock(_) => true,
TypeEnum::AsyncReset(_) => true, TypeEnum::AsyncReset(_) => true,
TypeEnum::SyncReset(_) => true, TypeEnum::SyncReset(_) => true,
TypeEnum::Reset(_) => false, // Reset is not castable from bits because we don't know if it's async or sync TypeEnum::Reset(_) => false, // Reset is not castable from bits because we don't know if it's async or sync
} TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
} }
pub fn bit_width(self) -> usize { pub fn bit_width(self) -> Result<usize, HitUndeducedType> {
match self { Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::bit_width(ty), TypeEnum::BundleType(ty) => DynBundleType::bit_width(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::bit_width(ty), TypeEnum::EnumType(ty) => DynEnumType::bit_width(ty)?,
TypeEnum::ArrayType(ty) => ArrayType::bit_width(&ty), TypeEnum::ArrayType(ty) => ArrayType::bit_width(&ty)?,
TypeEnum::UInt(ty) => ty.width, TypeEnum::UInt(ty) => ty.width,
TypeEnum::SInt(ty) => ty.width, TypeEnum::SInt(ty) => ty.width,
TypeEnum::Clock(ClockType) TypeEnum::Clock(ClockType)
| TypeEnum::AsyncReset(AsyncResetType) | TypeEnum::AsyncReset(AsyncResetType)
| TypeEnum::SyncReset(SyncResetType) | TypeEnum::SyncReset(SyncResetType)
| TypeEnum::Reset(ResetType) => 1, | TypeEnum::Reset(ResetType) => 1,
} TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
} }
pub fn bundle_type(self) -> Option<DynBundleType> { pub fn bundle_type(self) -> Option<DynBundleType> {
if let TypeEnum::BundleType(retval) = self { if let TypeEnum::BundleType(retval) = self {
@ -208,6 +216,13 @@ impl TypeEnum {
None None
} }
} }
pub fn undeduced(self) -> Option<UndeducedType> {
if let TypeEnum::Deduce(retval) = self {
Some(retval)
} else {
None
}
}
pub fn to_dyn(self) -> Interned<dyn DynType> { pub fn to_dyn(self) -> Interned<dyn DynType> {
match self { match self {
TypeEnum::BundleType(ty) => ty.to_dyn(), TypeEnum::BundleType(ty) => ty.to_dyn(),
@ -219,6 +234,7 @@ impl TypeEnum {
TypeEnum::AsyncReset(ty) => ty.to_dyn(), TypeEnum::AsyncReset(ty) => ty.to_dyn(),
TypeEnum::SyncReset(ty) => ty.to_dyn(), TypeEnum::SyncReset(ty) => ty.to_dyn(),
TypeEnum::Reset(ty) => ty.to_dyn(), TypeEnum::Reset(ty) => ty.to_dyn(),
TypeEnum::Deduce(ty) => ty.to_dyn(),
} }
} }
pub fn canonical_dyn(self) -> Interned<dyn DynCanonicalType> { pub fn canonical_dyn(self) -> Interned<dyn DynCanonicalType> {
@ -232,6 +248,7 @@ impl TypeEnum {
TypeEnum::AsyncReset(ty) => ty.canonical_dyn(), TypeEnum::AsyncReset(ty) => ty.canonical_dyn(),
TypeEnum::SyncReset(ty) => ty.canonical_dyn(), TypeEnum::SyncReset(ty) => ty.canonical_dyn(),
TypeEnum::Reset(ty) => ty.canonical_dyn(), TypeEnum::Reset(ty) => ty.canonical_dyn(),
TypeEnum::Deduce(ty) => ty.canonical_dyn(),
} }
} }
} }
@ -242,10 +259,10 @@ pub trait DynType: fmt::Debug + Send + Sync + Any + SupportsPtrEqWithTypeId {
fn mask_type_dyn(&self) -> Interned<dyn DynType>; fn mask_type_dyn(&self) -> Interned<dyn DynType>;
fn source_location_dyn(&self) -> SourceLocation; fn source_location_dyn(&self) -> SourceLocation;
fn type_enum_dyn(&self) -> TypeEnum; fn type_enum_dyn(&self) -> TypeEnum;
fn is_passive(&self) -> bool; fn is_passive(&self) -> Result<bool, HitUndeducedType>;
fn is_storable(&self) -> bool; fn is_storable(&self) -> Result<bool, HitUndeducedType>;
fn is_castable_from_bits(&self) -> bool; fn is_castable_from_bits(&self) -> Result<bool, HitUndeducedType>;
fn bit_width(&self) -> usize; fn bit_width(&self) -> Result<usize, HitUndeducedType>;
fn as_dyn_type(&self) -> &dyn DynType; fn as_dyn_type(&self) -> &dyn DynType;
fn as_dyn_canonical_type(&self) -> Option<&dyn DynCanonicalType>; fn as_dyn_canonical_type(&self) -> Option<&dyn DynCanonicalType>;
#[deprecated = "use <dyn DynType>::downcast or <dyn DynCanonicalType>::downcast instead, they properly handle Interned<dyn DynType> and Interned<dyn DynCanonicalType>"] #[deprecated = "use <dyn DynType>::downcast or <dyn DynCanonicalType>::downcast instead, they properly handle Interned<dyn DynType> and Interned<dyn DynCanonicalType>"]
@ -294,19 +311,19 @@ impl<T: Type> DynType for T {
self.type_enum() self.type_enum()
} }
fn is_passive(&self) -> bool { fn is_passive(&self) -> Result<bool, HitUndeducedType> {
self.type_enum().is_passive() self.type_enum().is_passive()
} }
fn is_storable(&self) -> bool { fn is_storable(&self) -> Result<bool, HitUndeducedType> {
self.type_enum().is_storable() self.type_enum().is_storable()
} }
fn is_castable_from_bits(&self) -> bool { fn is_castable_from_bits(&self) -> Result<bool, HitUndeducedType> {
self.type_enum().is_castable_from_bits() self.type_enum().is_castable_from_bits()
} }
fn bit_width(&self) -> usize { fn bit_width(&self) -> Result<usize, HitUndeducedType> {
self.type_enum().bit_width() self.type_enum().bit_width()
} }
@ -459,7 +476,9 @@ impl<T: 'static + Send + Sync> MatchVariantAndInactiveScope for MatchVariantWith
} }
} }
pub trait Type: DynType + Clone + Hash + Eq + Intern + Connect<Self> { pub trait Type:
DynType + Clone + Hash + Eq + Intern + Connect<Self> + Connect<UndeducedType>
{
type CanonicalType: CanonicalType< type CanonicalType: CanonicalType<
CanonicalType = Self::CanonicalType, CanonicalType = Self::CanonicalType,
CanonicalValue = Self::CanonicalValue, CanonicalValue = Self::CanonicalValue,
@ -562,8 +581,9 @@ pub trait CanonicalType:
fn can_connect(&self, other: &Self) -> bool { fn can_connect(&self, other: &Self) -> bool {
macro_rules! unwrap_other { macro_rules! unwrap_other {
($var:ident = $fn:ident()) => { ($var:ident = $fn:ident()) => {
let Some($var) = other.type_enum().$fn() else { let other_ty = other.type_enum();
return false; let Some($var) = other_ty.$fn() else {
return other_ty.undeduced().is_some();
}; };
}; };
} }
@ -619,6 +639,7 @@ pub trait CanonicalType:
unwrap_other!(other = reset()); unwrap_other!(other = reset());
this == other this == other
} }
TypeEnum::Deduce(_) => true,
} }
} }
} }
@ -641,7 +662,7 @@ pub trait DynValueTrait: fmt::Debug + Send + Sync + Any {
fn as_any(&self) -> &(dyn Any + Send + Sync); fn as_any(&self) -> &(dyn Any + Send + Sync);
fn as_dyn_value_trait(&self) -> &dyn DynValueTrait; fn as_dyn_value_trait(&self) -> &dyn DynValueTrait;
fn as_arc_dyn_value_trait(self: Arc<Self>) -> Arc<dyn DynValueTrait>; fn as_arc_dyn_value_trait(self: Arc<Self>) -> Arc<dyn DynValueTrait>;
fn to_bits(&self) -> Interned<BitSlice>; fn to_bits(&self) -> Result<Interned<BitSlice>, HitUndeducedType>;
} }
macro_rules! dyn_value { macro_rules! dyn_value {
@ -707,7 +728,7 @@ impl Value for DynValue {
self.0.to_canonical_dyn() self.0.to_canonical_dyn()
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
this.0.to_bits() this.0.to_bits()
} }
} }
@ -743,7 +764,7 @@ impl Value for DynCanonicalValue {
self.0.to_canonical_dyn() self.0.to_canonical_dyn()
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
<Self as CanonicalValue>::to_bits_impl(this) <Self as CanonicalValue>::to_bits_impl(this)
} }
} }
@ -759,7 +780,7 @@ impl CanonicalValue for DynCanonicalValue {
this.0.value_enum() this.0.value_enum()
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
this.0.to_bits() this.0.to_bits()
} }
} }
@ -807,7 +828,7 @@ pub trait Value: DynValueTrait + Clone + Eq + Hash + ToExpr {
fn valueless(&self) -> Valueless<Self::Type> { fn valueless(&self) -> Valueless<Self::Type> {
Valueless { ty: self.ty() } Valueless { ty: self.ty() }
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
<<Self::Type as Type>::CanonicalValue as CanonicalValue>::to_bits_impl(&this.to_canonical()) <<Self::Type as Type>::CanonicalValue as CanonicalValue>::to_bits_impl(&this.to_canonical())
} }
} }
@ -844,7 +865,7 @@ where
) )
} }
fn value_enum_impl(this: &Self) -> ValueEnum; fn value_enum_impl(this: &Self) -> ValueEnum;
fn to_bits_impl(this: &Self) -> Interned<BitSlice>; fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType>;
} }
impl<T: Value> DynValueTrait for T impl<T: Value> DynValueTrait for T
@ -884,11 +905,13 @@ where
self self
} }
fn to_bits(&self) -> Interned<BitSlice> { fn to_bits(&self) -> Result<Interned<BitSlice>, HitUndeducedType> {
T::to_bits_impl(self) T::to_bits_impl(self)
} }
} }
impl Connect<UndeducedType> for Interned<dyn DynType> {}
impl Type for Interned<dyn DynType> { impl Type for Interned<dyn DynType> {
type CanonicalType = Interned<dyn DynCanonicalType>; type CanonicalType = Interned<dyn DynCanonicalType>;
type Value = DynValue; type Value = DynValue;
@ -977,6 +1000,8 @@ impl Type for Interned<dyn DynCanonicalType> {
impl Connect<Self> for Interned<dyn DynCanonicalType> {} impl Connect<Self> for Interned<dyn DynCanonicalType> {}
impl Connect<UndeducedType> for Interned<dyn DynCanonicalType> {}
impl sealed::Sealed for Interned<dyn DynCanonicalType> {} impl sealed::Sealed for Interned<dyn DynCanonicalType> {}
impl CanonicalType for Interned<dyn DynCanonicalType> { impl CanonicalType for Interned<dyn DynCanonicalType> {
@ -1014,3 +1039,7 @@ impl sealed::Sealed for Array<[DynCanonicalValue]> {}
impl sealed::Sealed for DynEnumType {} impl sealed::Sealed for DynEnumType {}
impl sealed::Sealed for DynEnum {} impl sealed::Sealed for DynEnum {}
impl sealed::Sealed for UndeducedType {}
impl sealed::Sealed for Deduce {}

View file

@ -0,0 +1,136 @@
use crate::{
expr::{Expr, ToExpr},
intern::Interned,
source_location::SourceLocation,
ty::{
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, Type,
TypeEnum, Value, ValueEnum,
},
};
use bitvec::prelude::BitSlice;
use std::{
fmt,
sync::atomic::{AtomicU64, Ordering},
};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct HitUndeducedType;
impl HitUndeducedType {
/// if either input is [`Ok(false)`][Ok], return [`Ok(false)`][Ok],
/// otherwise if either input is [`Err(_)`][Err], return [`Err(_)`][Err],
/// otherwise return [`Ok(true)`][Ok].
pub fn reduce_and(l: Result<bool, Self>, r: Result<bool, Self>) -> Result<bool, Self> {
match (l, r) {
(Ok(false), _) | (_, Ok(false)) => Ok(false),
(Err(e), _) | (_, Err(e)) => Err(e),
(Ok(true), Ok(true)) => Ok(true),
}
}
}
impl fmt::Display for HitUndeducedType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("encountered a not-yet-deduced type")
}
}
impl std::error::Error for HitUndeducedType {}
#[derive(Copy, Debug, Clone, PartialEq, Eq, Hash)]
pub struct UndeducedType {
id: u64,
mask_ty_id: u64,
source_location: SourceLocation,
}
impl UndeducedType {
#[track_caller]
pub fn new() -> Self {
Self::new_with_loc(SourceLocation::caller())
}
pub fn new_with_loc(source_location: SourceLocation) -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
let id = NEXT_ID.fetch_add(2, Ordering::Relaxed);
let mask_ty_id = id + 1;
Self {
id,
mask_ty_id,
source_location,
}
}
pub fn id(self) -> u64 {
self.id
}
}
impl<T> Connect<T> for UndeducedType {}
impl Type for UndeducedType {
type CanonicalType = UndeducedType;
type Value = Deduce;
type CanonicalValue = Deduce;
type MaskType = UndeducedType;
type MaskValue = Deduce;
impl_match_values_as_self!();
fn mask_type(&self) -> Self::MaskType {
Self {
id: self.mask_ty_id,
mask_ty_id: self.mask_ty_id,
source_location: self.source_location,
}
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
self.source_location
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::Deduce(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
}
impl CanonicalType for UndeducedType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Deduce;
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub enum Deduce {}
impl ToExpr for Deduce {
type Type = UndeducedType;
fn ty(&self) -> Self::Type {
match *self {}
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
match *self {}
}
}
impl Value for Deduce {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
*self
}
}
impl CanonicalValue for Deduce {
fn value_enum_impl(this: &Self) -> ValueEnum {
match *this {}
}
fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
match *this {}
}
}

View file

@ -57,7 +57,8 @@
"Clock": "Visible", "Clock": "Visible",
"AsyncReset": "Visible", "AsyncReset": "Visible",
"SyncReset": "Visible", "SyncReset": "Visible",
"Reset": "Visible" "Reset": "Visible",
"Deduce": "Visible"
} }
}, },
"DynBundleType": { "DynBundleType": {
@ -120,6 +121,11 @@
"$kind": "Struct" "$kind": "Struct"
} }
}, },
"UndeducedType": {
"data": {
"$kind": "Opaque"
}
},
"AsyncResetType": { "AsyncResetType": {
"data": { "data": {
"$kind": "Struct" "$kind": "Struct"