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]
impl #static_type_impl_generics ::fayalite::ty::Type
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]
impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics
#where_clause

View file

@ -17,6 +17,7 @@ use crate::{
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
StaticValue, Type, TypeEnum, Value, ValueEnum,
},
type_deduction::{HitUndeducedType, UndeducedType},
util::{ConstBool, GenericConstBool, MakeMutSlice},
};
use bitvec::{slice::BitSlice, vec::BitVec};
@ -195,7 +196,7 @@ where
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
element: VA::ElementType,
len: VA::LenType,
bit_width: usize,
bit_width: Result<usize, HitUndeducedType>,
}
pub trait ArrayTypeTrait:
@ -210,7 +211,6 @@ pub trait ArrayTypeTrait:
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ sealed::Sealed
+ Connect<Self>
{
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
+ ?Sized;
@ -220,6 +220,8 @@ pub trait ArrayTypeTrait:
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> {
type ValueArrayOrSlice = VA;
type Element = VA::Element;
@ -248,7 +250,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn bit_width(&self) -> usize {
pub fn bit_width(&self) -> Result<usize, HitUndeducedType> {
self.bit_width
}
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
@ -266,10 +268,18 @@ impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
)
}
#[track_caller]
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
fn get_bit_width(
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");
};
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 {
element,
len,
@ -437,7 +447,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
.collect(),
}
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
fn to_bits_impl(this: &Self) -> Result<Interned<BitSlice>, HitUndeducedType> {
#[derive(Hash, Eq, PartialEq)]
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<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> {
type Input = Array<VA>;
type InputOwned = Array<VA>;
type Output = Interned<BitSlice>;
type Output = Result<Interned<BitSlice>, HitUndeducedType>;
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() {
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)
@ -467,7 +477,7 @@ impl CanonicalValue for Array<[DynCanonicalValue]> {
fn value_enum_impl(this: &Self) -> ValueEnum {
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)
}
}

View file

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

View file

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

View file

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

View file

@ -17,6 +17,7 @@ use crate::{
DynCanonicalType, DynCanonicalValue, DynType, DynValue, DynValueTrait, Type, TypeWithDeref,
Value,
},
type_deduction::HitUndeducedType,
util::ConstBool,
valueless::Valueless,
wire::Wire,
@ -51,8 +52,7 @@ macro_rules! expr_enum {
$(Self::$Variant(v) => v.target(),)+
}
}
#[allow(clippy::result_unit_err)]
pub fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
pub fn to_literal_bits(&self) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
match self {
$(Self::$Variant(v) => v.to_literal_bits(),)+
}
@ -176,6 +176,18 @@ pub struct Expr<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> {
pub fn expr_enum(self) -> ExprEnum {
self.__enum
@ -210,8 +222,9 @@ impl<T> Expr<T> {
{
Valueless { ty: self.ty() }
}
#[allow(clippy::result_unit_err)]
pub fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
pub fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
self.expr_enum().to_literal_bits()
}
#[track_caller]
@ -796,8 +809,9 @@ pub trait ExprTrait: ExprTraitBase {
fn valueless(&self) -> Valueless<Self::Type> {
Valueless { ty: self.ty() }
}
#[allow(clippy::result_unit_err)]
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()>;
fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr>;
}
impl<T: ExprTrait> ExprTraitBase for T {}
@ -875,7 +889,7 @@ impl<T: Type> Literal<T> {
#[track_caller]
pub fn new_unchecked(value: T::CanonicalValue) -> Self {
assert!(
value.ty().is_passive(),
value.ty().is_passive().unwrap_or(true),
"can't have a literal with flipped fields"
);
Self { value }
@ -918,7 +932,9 @@ impl<T: Type> ExprTrait for Literal<T> {
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())
}
}
@ -974,8 +990,10 @@ impl<T: Type> ExprTrait for ModuleIO<T> {
))
}
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
Err(())
fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
}
}
@ -1008,8 +1026,10 @@ where
Some(Intern::intern_sized(self.canonical().into()))
}
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
Err(())
fn to_literal_bits(
&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()))
}
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
Err(())
fn to_literal_bits(
&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()))
}
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
Err(())
fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
}
}
@ -1089,7 +1113,9 @@ where
fn target(&self) -> Option<Interned<Target>> {
Some(Intern::intern_sized(self.canonical().into()))
}
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, ()> {
Err(())
fn to_literal_bits(
&self,
) -> Result<Result<Interned<BitSlice>, HitUndeducedType>, NotALiteralExpr> {
Err(NotALiteralExpr)
}
}

View file

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

View file

@ -27,6 +27,7 @@ use crate::{
DynCanonicalType, DynCanonicalValue, DynCanonicalValueTrait, DynType, DynValue, Type,
TypeEnum, Value, ValueEnum,
},
type_deduction::HitUndeducedType,
util::{
const_str_array_is_strictly_ascending, BitSliceWriteWithBase, DebugAsRawString,
GenericConstBool,
@ -440,6 +441,7 @@ impl TypeState {
TypeEnum::AsyncReset(AsyncResetType {}) => "AsyncReset".into(),
TypeEnum::SyncReset(SyncResetType {}) => "UInt<1>".into(),
TypeEnum::Reset(ResetType {}) => "Reset".into(),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
}
}
}
@ -468,6 +470,25 @@ impl Default for ModuleState {
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 {
fn write_mem_init_file(
&mut self,
@ -1012,10 +1033,12 @@ impl<'a> Exporter<'a> {
name,
flipped: _,
ty: field_ty,
}| FieldType {
}| {
FieldType {
name,
flipped: false,
ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(),
ty: DynUIntType::new(field_ty.bit_width().unwrap_undeduced()).canonical_dyn(),
}
},
)));
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");
definitions.add_definition_line(format_args!(
"{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");
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");
definitions.add_definition_line(format_args!(
"{extra_indent}wire {retval}: UInt<{}>",
ty.bit_width()
ty.bit_width().unwrap_undeduced()
));
definitions.add_definition_line(format_args!("{extra_indent}match {value_str}:"));
let _match_arms_indent = extra_indent.push();
@ -1090,7 +1113,7 @@ impl<'a> Exporter<'a> {
definitions.add_definition_line(format_args!(
"{extra_indent}connect {retval}, pad(cat({variant_bits}, UInt<{}>({variant_index})), {})",
ty.discriminant_bit_width(),
ty.bit_width(),
ty.bit_width().unwrap_undeduced(),
));
} else {
definitions.add_definition_line(format_args!(
@ -1100,7 +1123,7 @@ impl<'a> Exporter<'a> {
let _match_arm_indent = extra_indent.push();
definitions.add_definition_line(format_args!(
"{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,
);
}
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");
definitions.add_definition_line(format_args!(
"{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");
definitions.add_definition_line(format_args!(
"{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");
definitions.add_definition_line(format_args!("{extra_indent}connect {retval}, {cat_expr}"));
@ -1178,6 +1201,7 @@ impl<'a> Exporter<'a> {
| TypeEnum::Clock(_)
| TypeEnum::AsyncReset(_)
| TypeEnum::Reset(_) => format!("asUInt({value_str})"),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
}
}
fn expr_cast_bits_to_bundle(
@ -1199,10 +1223,12 @@ impl<'a> Exporter<'a> {
name,
flipped: _,
ty: field_ty,
}| FieldType {
}| {
FieldType {
name,
flipped: false,
ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(),
ty: DynUIntType::new(field_ty.bit_width().unwrap_undeduced()).canonical_dyn(),
}
},
)));
let (flattened_ty_ident, _) = self.type_state.bundle_def(flattened_bundle_ty);
@ -1218,7 +1244,9 @@ impl<'a> Exporter<'a> {
.type_state
.get_bundle_field(flattened_bundle_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!(
"{extra_indent}connect {flattened_ident}.{flattened_field_ident}, bits({value_str}, {}, {field_offset})",
field_offset + field_bit_width_minus_one
@ -1272,7 +1300,7 @@ impl<'a> Exporter<'a> {
return retval.to_string();
}
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_value = if body_bit_width != 0 {
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 array_ty = self.type_state.ty(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 {
definitions.add_definition_line(format_args!("{extra_indent}invalidate {retval}"));
return retval.to_string();
@ -1382,6 +1410,7 @@ impl<'a> Exporter<'a> {
TypeEnum::AsyncReset(_) => format!("asAsyncReset({value_str})"),
TypeEnum::SyncReset(_) => value_str,
TypeEnum::Reset(_) => unreachable!("Reset is not bit castable to"),
TypeEnum::Deduce(_) => handle_undeduced_type(HitUndeducedType),
}
}
fn expr_unary<V: Value<Type: Type<Value = V>>>(
@ -1673,13 +1702,13 @@ impl<'a> Exporter<'a> {
) -> Result<(), WrappedError> {
assert_eq!(
initial_value.len(),
array_type.bit_width(),
array_type.bit_width().unwrap_undeduced(),
"literal bits don't match memory array type bit width"
);
if initial_value.is_empty() {
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 hex_or_binary = if element_bit_width % 4 == 0 {
HexOrBinary::Hex
@ -2391,7 +2420,7 @@ fn export_impl(
file_backend: &mut dyn WrappedFileBackendTrait,
top_module: Interned<Module<DynBundle>>,
) -> 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 mut global_ns = Namespace::default();
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,
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
},
type_deduction::{HitUndeducedType, UndeducedType},
util::{ConstBool, GenericConstBool},
valueless::Valueless,
};
@ -339,22 +340,7 @@ impl<
// correct since slice_and_shift ensures we're not trying to slice out of range
IntValue::with_type(ty, &self.uint_value >> shift)
}
}
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> {
pub fn to_bits(&self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> {
@ -387,7 +373,25 @@ impl<
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> Connect<UndeducedType> for DynIntType<Signed> {}
impl<Signed: GenericConstBool> Type for DynIntType<Signed> {
type CanonicalType = 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())
}
}
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)
}
}
@ -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 {
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> Connect<UndeducedType>
for IntType<Signed, WIDTH>
{
}
impl<Signed: GenericConstBool, const WIDTH: usize> Type for IntType<Signed, WIDTH> {
type CanonicalType = DynIntType<Signed>;
type Value = IntValue<IntType<Signed, WIDTH>>;

View file

@ -40,6 +40,7 @@ pub mod reg;
pub mod reset;
pub mod source_location;
pub mod ty;
pub mod type_deduction;
pub mod util;
pub mod valueless;
pub mod wire;

View file

@ -13,6 +13,7 @@ use crate::{
module::ScopedNameId,
source_location::SourceLocation,
ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value},
type_deduction::HitUndeducedType,
util::DebugAsDisplay,
};
use bitvec::slice::BitSlice;
@ -492,7 +493,7 @@ impl<T: PortType> MemPort<T> {
mem_element_type: Interned<dyn DynCanonicalType>,
) -> Self {
assert!(
mem_element_type.is_storable(),
mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type"
);
Self {
@ -626,7 +627,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
let addr_width = memory_addr_width(array_type.len());
let expected_mem_element_type = array_type.element().canonical_dyn();
assert!(
expected_mem_element_type.is_storable(),
expected_mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type"
);
for (index, port) in ports.iter().enumerate() {
@ -846,7 +847,8 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
initial_value: Interned<BitSlice>,
) -> Interned<BitSlice> {
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() {
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!(
@ -855,10 +857,11 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
"Mem's initializer bit length doesn't match the expected value",
);
}
}
assert!(
initial_value
.len()
.checked_rem(mem_element_type.bit_width())
.checked_rem(mem_element_type.bit_width().unwrap_or(1))
.unwrap_or(initial_value.len())
== 0,
"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 {
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!(
retval.len(),
Ok(retval.len()),
initial_value_ty.bit_width(),
"initial value produced wrong literal bits length"
);
@ -902,7 +914,7 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
let canonical_mem_element_type = mem_element_type.canonical_dyn();
assert!(
canonical_mem_element_type.is_storable(),
canonical_mem_element_type.is_storable().unwrap_or(true),
"memory element type must be a storable type"
);
let target = Rc::new(RefCell::new(MemBuilderTarget {
@ -1061,11 +1073,12 @@ impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
target.depth,
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 {
target.depth = Some(initial_value.len() / element_bit_width);
}
}
}
pub fn get_read_latency(&self) -> usize {
self.target.borrow().read_latency
}

View file

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

View file

@ -14,6 +14,7 @@ use crate::{
},
source_location::SourceLocation,
ty::{DynCanonicalType, DynCanonicalValue, Type, TypeEnum, Value, ValueEnum},
type_deduction::HitUndeducedType,
wire::Wire,
};
use core::fmt;
@ -22,6 +23,7 @@ use hashbrown::HashMap;
#[derive(Debug)]
pub enum SimplifyEnumsError {
EnumIsNotCastableFromBits { enum_type: DynEnumType },
HitUndeducedType(HitUndeducedType),
}
impl fmt::Display for SimplifyEnumsError {
@ -31,12 +33,19 @@ impl fmt::Display for SimplifyEnumsError {
f,
"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 From<HitUndeducedType> for SimplifyEnumsError {
fn from(value: HitUndeducedType) -> Self {
Self::HitUndeducedType(value)
}
}
#[derive(Value, Clone, Eq, PartialEq, Hash, Debug)]
struct TagAndBody<T> {
tag: T,
@ -69,7 +78,7 @@ impl State {
if let Some(retval) = self.enum_types.get(&enum_type) {
return Ok(retval.clone());
}
if !enum_type.is_castable_from_bits() {
if !enum_type.is_castable_from_bits()? {
return Err(SimplifyEnumsError::EnumIsNotCastableFromBits { enum_type });
}
let has_body = enum_type
@ -86,7 +95,7 @@ impl State {
},
))),
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> {
tag: DynUIntType::new(enum_type.discriminant_bit_width()),
body: DynUIntType::new(
enum_type.bit_width() - enum_type.discriminant_bit_width(),
enum_type.bit_width()? - enum_type.discriminant_bit_width(),
),
})
}
(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());
@ -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 {
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(
@ -120,7 +132,7 @@ fn connect_port(
lhs: Expr<DynCanonicalValue>,
rhs: Expr<DynCanonicalValue>,
source_location: SourceLocation,
) {
) -> Result<(), HitUndeducedType> {
println!("connect_port: lhs={lhs:?} rhs={rhs:?}");
if lhs.canonical_type() == rhs.canonical_type() {
stmts.push(
@ -131,24 +143,25 @@ fn connect_port(
})
.into(),
);
return;
return Ok(());
}
match (
lhs.canonical_type().type_enum(),
rhs.canonical_type().type_enum(),
) {
(TypeEnum::Deduce(_), _) | (_, TypeEnum::Deduce(_)) => return Err(HitUndeducedType),
(TypeEnum::BundleType(lhs_type), TypeEnum::UInt(_)) => {
let lhs = lhs.with_type::<DynBundle>();
for field in lhs_type.fields() {
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)) => {
let rhs = rhs.with_type::<DynBundle>();
for field in rhs_type.fields() {
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(_)) => {
@ -160,14 +173,14 @@ fn connect_port(
} else {
(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(_)) => {
let lhs = lhs.with_type::<Array<[DynCanonicalValue]>>();
let rhs = rhs.with_type::<Array<[DynCanonicalValue]>>();
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(_), _)
@ -184,6 +197,7 @@ fn connect_port(
rhs.canonical_type().type_enum(),
),
}
Ok(())
}
impl Folder for State {
@ -240,7 +254,7 @@ impl Folder for State {
Some(variant_value) => variant_value.fold(self)?.cast_to_bits(),
None => DynUInt::with_type(
DynUIntType::new(
op.ty().bit_width() - op.ty().discriminant_bit_width(),
op.ty().bit_width()? - op.ty().discriminant_bit_width(),
),
0u8,
)
@ -268,7 +282,7 @@ impl Folder for State {
.fold(self)?
.to_expr()
.with_type::<TagAndBody<DynCanonicalValue>>()
.body[..op.ty().bit_width()]
.body[..op.ty().bit_width()?]
.cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum(),
None => ().to_expr().expr_enum(),
@ -284,7 +298,7 @@ impl Folder for State {
.with_type::<DynUInt>();
dbg!(base_int);
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]
.cast_bits_to::<DynCanonicalValue>(op.ty())
.expr_enum()
@ -428,7 +442,7 @@ impl Folder for State {
new_port.to_expr().to_canonical_dyn(),
wire.to_expr().to_canonical_dyn(),
port.source_location(),
);
)?;
self.replacement_mem_ports
.insert(port, wire.to_dyn_canonical_wire().intern_sized());
}
@ -562,6 +576,7 @@ impl Folder for State {
| TypeEnum::AsyncReset(_)
| TypeEnum::SyncReset(_)
| TypeEnum::Reset(_) => type_enum.default_fold(self),
TypeEnum::Deduce(_) => Err(HitUndeducedType.into()),
}
}
@ -575,7 +590,7 @@ impl Folder for State {
}) => ValueEnum::Bundle(
TagAndBody {
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(),
),
@ -585,12 +600,12 @@ impl Folder for State {
}) => ValueEnum::Bundle(
TagAndBody {
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(),
),
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),
})

View file

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

View file

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

View file

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

View file

@ -14,6 +14,7 @@ use crate::{
},
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
source_location::SourceLocation,
type_deduction::{Deduce, HitUndeducedType, UndeducedType},
util::{iter_eq_by, GenericConstBool},
valueless::Valueless,
};
@ -46,6 +47,7 @@ pub enum TypeEnum {
AsyncReset(AsyncResetType),
SyncReset(SyncResetType),
Reset(ResetType),
Deduce(UndeducedType),
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
@ -60,6 +62,7 @@ pub enum CanonicalTypeKind {
SyncReset,
Reset,
DynCanonicalType,
Deduce,
}
impl CanonicalTypeKind {
@ -75,6 +78,7 @@ impl CanonicalTypeKind {
CanonicalTypeKind::SyncReset => "Expr<SyncReset>",
CanonicalTypeKind::Reset => "Expr<Reset>",
CanonicalTypeKind::DynCanonicalType => "Expr<DynCanonicalValue>",
CanonicalTypeKind::Deduce => "Expr<Deduce>",
}
}
}
@ -93,57 +97,61 @@ pub enum ValueEnum {
}
impl TypeEnum {
pub fn is_passive(self) -> bool {
match self {
TypeEnum::BundleType(ty) => DynBundleType::is_passive(ty),
TypeEnum::EnumType(ty) => DynEnumType::is_passive(ty),
TypeEnum::ArrayType(ty) => ty.element().is_passive(),
pub fn is_passive(self) -> Result<bool, HitUndeducedType> {
Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_passive(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_passive(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_passive()?,
TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => true,
TypeEnum::AsyncReset(_) => true,
TypeEnum::SyncReset(_) => true,
TypeEnum::Reset(_) => true,
TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
}
}
pub fn is_storable(self) -> bool {
match self {
TypeEnum::BundleType(ty) => DynBundleType::is_storable(ty),
TypeEnum::EnumType(ty) => DynEnumType::is_storable(ty),
TypeEnum::ArrayType(ty) => ty.element().is_storable(),
pub fn is_storable(self) -> Result<bool, HitUndeducedType> {
Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_storable(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_storable(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_storable()?,
TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => false,
TypeEnum::AsyncReset(_) => false,
TypeEnum::SyncReset(_) => false,
TypeEnum::Reset(_) => false,
TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
}
}
pub fn is_castable_from_bits(self) -> bool {
match self {
TypeEnum::BundleType(ty) => DynBundleType::is_castable_from_bits(ty),
TypeEnum::EnumType(ty) => DynEnumType::is_castable_from_bits(ty),
TypeEnum::ArrayType(ty) => ty.element().is_castable_from_bits(),
pub fn is_castable_from_bits(self) -> Result<bool, HitUndeducedType> {
Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::is_castable_from_bits(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::is_castable_from_bits(ty)?,
TypeEnum::ArrayType(ty) => ty.element().is_castable_from_bits()?,
TypeEnum::UInt(_) => true,
TypeEnum::SInt(_) => true,
TypeEnum::Clock(_) => true,
TypeEnum::AsyncReset(_) => 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::Deduce(_) => return Err(HitUndeducedType),
})
}
}
pub fn bit_width(self) -> usize {
match self {
TypeEnum::BundleType(ty) => DynBundleType::bit_width(ty),
TypeEnum::EnumType(ty) => DynEnumType::bit_width(ty),
TypeEnum::ArrayType(ty) => ArrayType::bit_width(&ty),
pub fn bit_width(self) -> Result<usize, HitUndeducedType> {
Ok(match self {
TypeEnum::BundleType(ty) => DynBundleType::bit_width(ty)?,
TypeEnum::EnumType(ty) => DynEnumType::bit_width(ty)?,
TypeEnum::ArrayType(ty) => ArrayType::bit_width(&ty)?,
TypeEnum::UInt(ty) => ty.width,
TypeEnum::SInt(ty) => ty.width,
TypeEnum::Clock(ClockType)
| TypeEnum::AsyncReset(AsyncResetType)
| TypeEnum::SyncReset(SyncResetType)
| TypeEnum::Reset(ResetType) => 1,
}
TypeEnum::Deduce(_) => return Err(HitUndeducedType),
})
}
pub fn bundle_type(self) -> Option<DynBundleType> {
if let TypeEnum::BundleType(retval) = self {
@ -208,6 +216,13 @@ impl TypeEnum {
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> {
match self {
TypeEnum::BundleType(ty) => ty.to_dyn(),
@ -219,6 +234,7 @@ impl TypeEnum {
TypeEnum::AsyncReset(ty) => ty.to_dyn(),
TypeEnum::SyncReset(ty) => ty.to_dyn(),
TypeEnum::Reset(ty) => ty.to_dyn(),
TypeEnum::Deduce(ty) => ty.to_dyn(),
}
}
pub fn canonical_dyn(self) -> Interned<dyn DynCanonicalType> {
@ -232,6 +248,7 @@ impl TypeEnum {
TypeEnum::AsyncReset(ty) => ty.canonical_dyn(),
TypeEnum::SyncReset(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 source_location_dyn(&self) -> SourceLocation;
fn type_enum_dyn(&self) -> TypeEnum;
fn is_passive(&self) -> bool;
fn is_storable(&self) -> bool;
fn is_castable_from_bits(&self) -> bool;
fn bit_width(&self) -> usize;
fn is_passive(&self) -> Result<bool, HitUndeducedType>;
fn is_storable(&self) -> Result<bool, HitUndeducedType>;
fn is_castable_from_bits(&self) -> Result<bool, HitUndeducedType>;
fn bit_width(&self) -> Result<usize, HitUndeducedType>;
fn as_dyn_type(&self) -> &dyn DynType;
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>"]
@ -294,19 +311,19 @@ impl<T: Type> DynType for T {
self.type_enum()
}
fn is_passive(&self) -> bool {
fn is_passive(&self) -> Result<bool, HitUndeducedType> {
self.type_enum().is_passive()
}
fn is_storable(&self) -> bool {
fn is_storable(&self) -> Result<bool, HitUndeducedType> {
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()
}
fn bit_width(&self) -> usize {
fn bit_width(&self) -> Result<usize, HitUndeducedType> {
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<
CanonicalType = Self::CanonicalType,
CanonicalValue = Self::CanonicalValue,
@ -562,8 +581,9 @@ pub trait CanonicalType:
fn can_connect(&self, other: &Self) -> bool {
macro_rules! unwrap_other {
($var:ident = $fn:ident()) => {
let Some($var) = other.type_enum().$fn() else {
return false;
let other_ty = other.type_enum();
let Some($var) = other_ty.$fn() else {
return other_ty.undeduced().is_some();
};
};
}
@ -619,6 +639,7 @@ pub trait CanonicalType:
unwrap_other!(other = reset());
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_dyn_value_trait(&self) -> &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 {
@ -707,7 +728,7 @@ impl Value for DynValue {
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()
}
}
@ -743,7 +764,7 @@ impl Value for DynCanonicalValue {
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)
}
}
@ -759,7 +780,7 @@ impl CanonicalValue for DynCanonicalValue {
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()
}
}
@ -807,7 +828,7 @@ pub trait Value: DynValueTrait + Clone + Eq + Hash + ToExpr {
fn valueless(&self) -> Valueless<Self::Type> {
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())
}
}
@ -844,7 +865,7 @@ where
)
}
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
@ -884,11 +905,13 @@ where
self
}
fn to_bits(&self) -> Interned<BitSlice> {
fn to_bits(&self) -> Result<Interned<BitSlice>, HitUndeducedType> {
T::to_bits_impl(self)
}
}
impl Connect<UndeducedType> for Interned<dyn DynType> {}
impl Type for Interned<dyn DynType> {
type CanonicalType = Interned<dyn DynCanonicalType>;
type Value = DynValue;
@ -977,6 +1000,8 @@ impl Type 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 CanonicalType for Interned<dyn DynCanonicalType> {
@ -1014,3 +1039,7 @@ impl sealed::Sealed for Array<[DynCanonicalValue]> {}
impl sealed::Sealed for DynEnumType {}
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",
"AsyncReset": "Visible",
"SyncReset": "Visible",
"Reset": "Visible"
"Reset": "Visible",
"Deduce": "Visible"
}
},
"DynBundleType": {
@ -120,6 +121,11 @@
"$kind": "Struct"
}
},
"UndeducedType": {
"data": {
"$kind": "Opaque"
}
},
"AsyncResetType": {
"data": {
"$kind": "Struct"