diff --git a/crates/fayalite-proc-macros-impl/src/value_derive_enum.rs b/crates/fayalite-proc-macros-impl/src/value_derive_enum.rs index 87f53c1..4c6d66b 100644 --- a/crates/fayalite-proc-macros-impl/src/value_derive_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/value_derive_enum.rs @@ -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 diff --git a/crates/fayalite-proc-macros-impl/src/value_derive_struct.rs b/crates/fayalite-proc-macros-impl/src/value_derive_struct.rs index a0b303a..6dd82d2 100644 --- a/crates/fayalite-proc-macros-impl/src/value_derive_struct.rs +++ b/crates/fayalite-proc-macros-impl/src/value_derive_struct.rs @@ -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 diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 3763cae..642914b 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -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 { element: VA::ElementType, len: VA::LenType, - bit_width: usize, + bit_width: Result, } pub trait ArrayTypeTrait: @@ -210,7 +211,6 @@ pub trait ArrayTypeTrait: + Into::ValueArrayOrSlice>> + BorrowMut::ValueArrayOrSlice>> + sealed::Sealed - + Connect { type ValueArrayOrSlice: ValueArrayOrSlice + ?Sized; @@ -220,6 +220,8 @@ pub trait ArrayTypeTrait: impl sealed::Sealed for ArrayType {} +impl Connect for ArrayType {} + impl ArrayTypeTrait for ArrayType { type ValueArrayOrSlice = VA; type Element = VA::Element; @@ -248,7 +250,7 @@ impl ArrayType { pub fn is_empty(&self) -> bool { self.len() == 0 } - pub fn bit_width(&self) -> usize { + pub fn bit_width(&self) -> Result { self.bit_width } pub fn into_slice_type(self) -> ArrayType<[VA::Element]> { @@ -266,10 +268,18 @@ impl ArrayType { ) } #[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 { + 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 Value for Array { .collect(), } } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { #[derive(Hash, Eq, PartialEq)] struct ArrayToBitsMemoize(PhantomData); impl Clone for ArrayToBitsMemoize { @@ -449,14 +459,14 @@ impl Value for Array { impl Memoize for ArrayToBitsMemoize { type Input = Array; type InputOwned = Array; - type Output = Interned; + type Output = Result, 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::(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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { Value::to_bits_impl(this) } } diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 2d43031..ed753c5 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -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 { pub struct FmtDebugInStruct<'a, T> { field: &'a FieldType, - field_offset: usize, + field_offset: Option, } impl fmt::Debug for FmtDebugInStruct<'_, T> { @@ -49,7 +50,9 @@ impl fmt::Debug for FmtDebugInStruct<'_, T> { write!(f, "#[hdl(flip)] ")?; } if f.alternate() { - writeln!(f, "/* offset = {field_offset} */")?; + if let Some(field_offset) = field_offset { + writeln!(f, "/* offset = {field_offset} */")?; + } } write!(f, "{name}: ")?; ty.fmt(f) @@ -78,7 +81,7 @@ impl FieldType { 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) -> FmtDebugInStruct<'_, T> { FmtDebugInStruct { field: self, field_offset, @@ -135,22 +138,19 @@ struct DynBundleTypeImpl { fields: Interned<[FieldType>]>, name_indexes: HashMap, usize>, field_offsets: Interned<[usize]>, - is_passive: bool, - is_storable: bool, - is_castable_from_bits: bool, - bit_width: usize, + is_passive: Result, + is_storable: Result, + is_castable_from_bits: Result, + bit_width: Result, } 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>]>) -> Self { - let is_passive = fields - .iter() - .all(|field| !field.flipped && field.ty.is_passive()); - let is_storable = fields - .iter() - .all(|field| !field.flipped && field.ty.is_storable()); - let is_castable_from_bits = fields - .iter() - .all(|field| !field.flipped && field.ty.is_castable_from_bits()); + fn calc_prop( + fields: Interned<[FieldType>]>, + property: impl Fn(&Interned) -> Result, + ) -> Result { + fields + .iter() + .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 { self.0.is_passive } - pub fn is_storable(self) -> bool { + pub fn is_storable(self) -> Result { self.0.is_storable } - pub fn is_castable_from_bits(self) -> bool { + pub fn is_castable_from_bits(self) -> Result { self.0.is_castable_from_bits } - pub fn bit_width(self) -> usize { + pub fn bit_width(self) -> Result { self.0.bit_width } pub fn name_indexes(&self) -> &HashMap, usize> { @@ -370,7 +383,7 @@ impl FieldsHint { } pub trait BundleType: - Type + TypeWithDeref + Connect + Type + TypeWithDeref where Self::Value: BundleValue + ToExpr, { @@ -384,7 +397,7 @@ pub trait BundleValue: Value where ::Type: BundleType, { - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { #[derive(Hash, Eq, PartialEq)] struct ToBitsMemoize(PhantomData); impl Clone for ToBitsMemoize { @@ -396,21 +409,23 @@ where impl>> Memoize for ToBitsMemoize { type Input = T; type InputOwned = T; - type Output = Interned; + type Output = Result, 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::(PhantomData).get(this) } } +impl Connect for DynBundleType {} + pub struct DynBundleMatch; impl Type for DynBundleType { @@ -534,7 +549,7 @@ impl Value for DynBundle { fn to_canonical(&self) -> ::CanonicalValue { self.clone() } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, 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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { BundleValue::to_bits_impl(this) } } @@ -645,6 +660,8 @@ macro_rules! impl_tuple { } } + impl<$($T,)*> Connect 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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { BundleValue::to_bits_impl(this) } } diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 415d7b6..48c1a3c 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -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 for ClockType {} + impl Type for ClockType { type Value = Clock; type CanonicalType = ClockType; @@ -88,8 +91,8 @@ impl Value for Clock { fn to_canonical(&self) -> ::CanonicalValue { *self } - fn to_bits_impl(this: &Self) -> Interned { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, 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 { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { + Ok(interned_bit(this.0)) } } diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index 6b9c104..1e6180d 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -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> { struct DynEnumTypeImpl { variants: Interned<[VariantType>]>, name_indexes: HashMap, usize>, - bit_width: usize, - is_storable: bool, - is_castable_from_bits: bool, + bit_width: Result, + is_storable: Result, + is_castable_from_bits: Result, } impl fmt::Debug for DynEnumTypeImpl { @@ -158,26 +159,28 @@ impl DynEnumType { pub fn new(variants: Interned<[VariantType>]>) -> 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 { + Ok(true) } - pub fn is_storable(self) -> bool { + pub fn is_storable(self) -> Result { self.0.is_storable } - pub fn is_castable_from_bits(self) -> bool { + pub fn is_castable_from_bits(self) -> Result { self.0.is_castable_from_bits } - pub fn bit_width(self) -> usize { + pub fn bit_width(self) -> Result { self.0.bit_width } pub fn name_indexes(&self) -> &HashMap, usize> { @@ -323,14 +326,14 @@ impl VariantsHint { pub trait EnumType: Type< - CanonicalType = DynEnumType, - CanonicalValue = DynEnum, - MaskType = UIntType<1>, - MaskValue = UInt<1>, - MatchActiveScope = Scope, - MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, - MatchVariantsIter = EnumMatchVariantsIter, - > + Connect + CanonicalType = DynEnumType, + CanonicalValue = DynEnum, + MaskType = UIntType<1>, + MaskValue = UInt<1>, + MatchActiveScope = Scope, + MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope, + MatchVariantsIter = EnumMatchVariantsIter, +> where Self::Value: EnumValue + ToExpr, { @@ -346,7 +349,7 @@ where &self, variant_index: usize, variant_value: Option<&VariantValue>, - ) -> Result, ()> { + ) -> Result, HitUndeducedType>, NotALiteralExpr> { #[derive(Hash, Eq, PartialEq)] struct VariantToBitsMemoize(PhantomData<(E, V)>); impl Clone for VariantToBitsMemoize { @@ -363,7 +366,7 @@ where type InputRef<'a> = (&'a E, usize, Option<&'a V>); type InputOwned = (E, usize, Option); type InputCow<'a> = (Cow<'a, E>, usize, Option>); - type Output = Result, ()>; + type Output = Result, 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::()[..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::(PhantomData).get(( @@ -492,6 +502,8 @@ where } } +impl Connect for DynEnumType {} + impl Type for DynEnumType { type CanonicalType = DynEnumType; type Value = DynEnum; @@ -589,7 +601,7 @@ impl Value for DynEnum { fn to_canonical(&self) -> ::CanonicalValue { self.clone() } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, 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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { this.ty .variant_to_bits(this.variant_index, this.variant_value.as_ref()) .unwrap() diff --git a/crates/fayalite/src/expr.rs b/crates/fayalite/src/expr.rs index 2b8f2b2..5bc6c63 100644 --- a/crates/fayalite/src/expr.rs +++ b/crates/fayalite/src/expr.rs @@ -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, ()> { + pub fn to_literal_bits(&self) -> Result, HitUndeducedType>, NotALiteralExpr> { match self { $(Self::$Variant(v) => v.to_literal_bits(),)+ } @@ -176,6 +176,18 @@ pub struct Expr { __phantom: PhantomData, } +#[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 Expr { pub fn expr_enum(self) -> ExprEnum { self.__enum @@ -210,8 +222,9 @@ impl Expr { { Valueless { ty: self.ty() } } - #[allow(clippy::result_unit_err)] - pub fn to_literal_bits(&self) -> Result, ()> { + pub fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.expr_enum().to_literal_bits() } #[track_caller] @@ -796,8 +809,9 @@ pub trait ExprTrait: ExprTraitBase { fn valueless(&self) -> Valueless { Valueless { ty: self.ty() } } - #[allow(clippy::result_unit_err)] - fn to_literal_bits(&self) -> Result, ()>; + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr>; } impl ExprTraitBase for T {} @@ -875,7 +889,7 @@ impl Literal { #[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 ExprTrait for Literal { None } - fn to_literal_bits(&self) -> Result, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { Ok(self.value.to_bits()) } } @@ -974,8 +990,10 @@ impl ExprTrait for ModuleIO { )) } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { + Err(NotALiteralExpr) } } @@ -1008,8 +1026,10 @@ where Some(Intern::intern_sized(self.canonical().into())) } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { + Err(NotALiteralExpr) } } @@ -1024,8 +1044,10 @@ impl ExprTrait for Wire { Some(Intern::intern_sized(self.to_dyn_canonical_wire().into())) } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { + Err(NotALiteralExpr) } } @@ -1040,8 +1062,10 @@ impl ExprTrait for Reg { Some(Intern::intern_sized(self.to_dyn_canonical_reg().into())) } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { + Err(NotALiteralExpr) } } @@ -1089,7 +1113,9 @@ where fn target(&self) -> Option> { Some(Intern::intern_sized(self.canonical().into())) } - fn to_literal_bits(&self) -> Result, ()> { - Err(()) + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { + Err(NotALiteralExpr) } } diff --git a/crates/fayalite/src/expr/ops.rs b/crates/fayalite/src/expr/ops.rs index c1a16f1..6f1523f 100644 --- a/crates/fayalite/src/expr/ops.rs +++ b/crates/fayalite/src/expr/ops.rs @@ -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: Expr, + f: impl FnOnce(Interned) -> Interned, +) -> Result, HitUndeducedType>, NotALiteralExpr> { + Ok(v.to_literal_bits()?.map(f)) +} + +fn try_unary_literal_bits>>( + v: Expr, + f: impl FnOnce(Interned) -> Result, HitUndeducedType>, +) -> Result, HitUndeducedType>, NotALiteralExpr> { + Ok(v.to_literal_bits()?.and_then(f)) +} + +fn binary_literal_bits>, R: Value>>( + l: Expr, + r: Expr, + f: impl FnOnce(Interned, Interned) -> Interned, +) -> Result, HitUndeducedType>, NotALiteralExpr> { + let l = l.to_literal_bits()?; + let r = r.to_literal_bits()?; + Ok((|| Ok(f(l?, r?)))()) +} + +fn try_binary_literal_bits>, R: Value>>( + l: Expr, + r: Expr, + f: impl FnOnce( + Interned, + Interned, + ) -> Result, HitUndeducedType>, +) -> Result, 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, ()> { + fn to_literal_bits( + &$to_literal_bits_self:ident, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { $($to_literal_bits_body:tt)+ } } @@ -173,7 +212,9 @@ macro_rules! fixed_ary_op { fn target(&self) -> Option> { ($(self.$target_name,)? None::>,).0 } - fn to_literal_bits(&$to_literal_bits_self) -> Result, ()> { + fn to_literal_bits( + &$to_literal_bits_self, + ) -> Result, 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, ()> = { - arg.to_literal_bits() - .map(|v| ops::$Op::$op($T::CanonicalValue::from_bit_slice(&v)).to_bits()) + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -306,18 +348,14 @@ macro_rules! binary_op { Valueless::<$RhsType>::from_canonical(rhs.valueless()), ).ty, #[cache] - literal_bits: Result, ()> = { - lhs.to_literal_bits() - .ok() - .zip(rhs.to_literal_bits().ok()) - .map(|(lhs, rhs)| { - ops::$Op::$op( - $LhsType::CanonicalValue::from_bit_slice(&lhs), - $RhsType::CanonicalValue::from_bit_slice(&rhs), - ) - .to_bits() - }) - .ok_or(()) + literal_bits: Result, 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() + }) }, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -539,18 +579,14 @@ macro_rules! dyn_shift_op { rhs.valueless(), ).ty, #[cache] - literal_bits: Result, ()> = { - lhs.to_literal_bits() - .ok() - .zip(rhs.to_literal_bits().ok()) - .map(|(lhs, rhs)| { - ops::$Op::$op( - LhsType::CanonicalValue::from_bit_slice(&lhs), - DynUInt::from_bit_slice(&rhs), - ) - .to_bits() - }) - .ok_or(()) + literal_bits: Result, 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() + }) }, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -646,15 +684,14 @@ macro_rules! fixed_shift_op { ty: < as ops::$Op>::Output as ValuelessTr>::Type = ops::$Op::$op(lhs.valueless(), rhs).ty, #[cache] - literal_bits: Result, ()> = { - lhs.to_literal_bits() - .map(|lhs| { - ops::$Op::$op( - LhsType::CanonicalValue::from_bit_slice(&lhs), - rhs, - ) - .to_bits() - }) + literal_bits: Result, HitUndeducedType>, NotALiteralExpr> = { + unary_literal_bits(lhs, |lhs| { + ops::$Op::$op( + LhsType::CanonicalValue::from_bit_slice(&lhs), + rhs, + ) + .to_bits() + }) }, fn simulate(&self, sim_state: &mut SimState) -> _ { 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()), } } - fn to_literal_bits(&self) -> Result, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -739,18 +778,14 @@ macro_rules! cmp_op { #[type] ty: Output = StaticOrDynIntType::new(), #[cache] - literal_bits: Result, ()> = { - lhs.to_literal_bits() - .ok() - .zip(rhs.to_literal_bits().ok()) - .map(|(lhs, rhs)| { - interned_bit( - LhsType::CanonicalValue::from_bit_slice(&lhs) - .value() - .$op(&RhsType::CanonicalValue::from_bit_slice(&rhs).value()), - ) - }) - .ok_or(()) + literal_bits: Result, 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()), + ) + }) }, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -856,8 +893,8 @@ fixed_ary_op! { #[type(ty)] pub ty: ToType = ty, #[cache] - literal_bits: Result, ()> = { - value.to_literal_bits().map(|literal_bits| { + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, 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, ()> = { - base.to_literal_bits().map(|base| { + literal_bits: Result, HitUndeducedType>, NotALiteralExpr> = { + unary_literal_bits(base, |base| { base[range.clone()].intern() }) }, @@ -976,7 +1015,9 @@ fixed_ary_op! { } } - fn to_literal_bits(&self) -> Result, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1110,8 +1151,8 @@ macro_rules! reduce_bit_op { #[type] ty: Output = StaticOrDynIntType::new(), #[cache] - literal_bits: Result, ()> = { - arg.to_literal_bits().map(|$bits| interned_bit($literal_bits_bool)) + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, 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, ()> = { - base.to_literal_bits().map(|base| { - base[base_ty.field_offsets()[index]..][..field.ty.bit_width()].intern() + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, 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, ()> = { - base.to_literal_bits().map(|base| { - base[base_ty.discriminant_bit_width()..][..ty.bit_width()].intern() + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1292,7 +1339,7 @@ fixed_ary_op! { pub struct CastToBits<> where () { value: Expr, #[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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.value.to_literal_bits() } } @@ -1327,8 +1376,10 @@ fixed_ary_op! { value: Expr, #[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, ()> { + fn to_literal_bits( + &self, + ) -> Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.value.to_literal_bits() } } @@ -1552,17 +1607,18 @@ fixed_ary_op! { ty }, #[cache] - literal_bits: Result, ()> = { - let mut bits = Some(BitVec::with_capacity(ty.bit_width())); + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1603,13 +1661,13 @@ fixed_ary_op! { base_ty.element().clone() }, #[cache] - literal_bits: Result, ()> = { + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1655,18 +1715,32 @@ fixed_ary_op! { base_ty.element().clone() }, #[cache] - literal_bits: Result, ()> = { + literal_bits: Result, 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 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(()) - }, + 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() + .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> = base.target().map(|base| { Intern::intern_sized(base.join( @@ -1683,7 +1757,9 @@ fixed_ary_op! { ) } - fn to_literal_bits(&self) -> Result, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1728,7 +1804,7 @@ impl< fn expr_index(this: Expr, index: Expr>) -> Expr { let index: Expr = 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, ()> = { - let mut bits = Some(BitVec::new()); + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } @@ -1831,7 +1910,7 @@ fixed_ary_op! { ty }, #[cache] - literal_bits: Result, ()> = + literal_bits: Result, 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, ()> { + fn to_literal_bits( + &self, + ) -> Result, HitUndeducedType>, NotALiteralExpr> { self.literal_bits } } diff --git a/crates/fayalite/src/firrtl.rs b/crates/fayalite/src/firrtl.rs index cd53f1d..ee1b289 100644 --- a/crates/fayalite/src/firrtl.rs +++ b/crates/fayalite/src/firrtl.rs @@ -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 UnwrapUndeduced for Result { + 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 { - name, - flipped: false, - ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(), + }| { + FieldType { + name, + 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); @@ -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 { - name, - flipped: false, - ty: DynUIntType::new(field_ty.bit_width()).canonical_dyn(), + }| { + FieldType { + name, + 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); @@ -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>>( @@ -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>, ) -> 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()); diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index ce7d96f..6798386 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -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<::Signed>, - CanonicalValue = DynInt<::Signed>, - >, - > Value for IntValue -{ - fn to_canonical(&self) -> ::CanonicalValue { - IntValue { - ty: self.ty.canonical(), - uint_value: self.uint_value.clone(), - } - } - fn to_bits_impl(this: &Self) -> Interned { + pub fn to_bits(&self) -> Interned { #[derive(Hash, Eq, PartialEq)] struct ToBitsMemoize(PhantomData); impl Clone for ToBitsMemoize { @@ -387,7 +373,25 @@ impl< Intern::intern_owned(bits) } } - ToBitsMemoize::(PhantomData).get(this) + ToBitsMemoize::(PhantomData).get(self) + } +} + +impl< + Ty: IntTypeTrait< + CanonicalType = DynIntType<::Signed>, + CanonicalValue = DynInt<::Signed>, + >, + > Value for IntValue +{ + fn to_canonical(&self) -> ::CanonicalValue { + IntValue { + ty: self.ty.canonical(), + uint_value: self.uint_value.clone(), + } + } + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { + Ok(IntValue::to_bits(this)) } } @@ -1111,6 +1115,8 @@ impl DynSIntType { impl sealed::Sealed for DynIntType {} +impl Connect for DynIntType {} + impl Type for DynIntType { type CanonicalType = DynIntType; type Value = IntValue>; @@ -1176,7 +1182,7 @@ impl CanonicalValue for IntValue> { ValueEnum::UInt(this.clone().as_same_width_uint()) } } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { ::to_bits_impl(this) } } @@ -1203,7 +1209,9 @@ impl IntTypeTrait for DynIntType { } } -impl StaticOrDynIntType for DynIntType { +impl StaticOrDynIntType + for DynIntType +{ fn new() -> Self { DynIntType::new(WIDTH) } @@ -1233,6 +1241,11 @@ pub type SIntType = IntType, WIDTH>; impl sealed::Sealed for IntType {} +impl Connect + for IntType +{ +} + impl Type for IntType { type CanonicalType = DynIntType; type Value = IntValue>; diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index c0a3775..03e6967 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -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; diff --git a/crates/fayalite/src/memory.rs b/crates/fayalite/src/memory.rs index 6314623..7ec932f 100644 --- a/crates/fayalite/src/memory.rs +++ b/crates/fayalite/src/memory.rs @@ -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 MemPort { mem_element_type: Interned, ) -> 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 Mem { 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,19 +847,21 @@ impl MemBuilder { initial_value: Interned, ) -> Interned { if let Some(depth) = depth { - let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect( - "memory must be small enough that its initializer bit length fits in usize", - ); - assert_eq!( - expected_len, - initial_value.len(), - "Mem's initializer bit length doesn't match the expected value", - ); + 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!( + expected_len, + initial_value.len(), + "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 MemBuilder { 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 MemBuilder { ) -> (Self, Rc>) { 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,9 +1073,10 @@ impl MemBuilder { target.depth, initial_value, )); - let element_bit_width = self.mem_element_type.bit_width(); - if element_bit_width != 0 { - target.depth = Some(initial_value.len() / element_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 { diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index 178bd25..ff412c0 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -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, declared_in_block: usize) -> Self { - Self { + fn new(target: Interned, declared_in_block: usize) -> Result { + 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::>()?, }, 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::>()?, }, 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, declared_in_block: usize) { + fn insert_new_base( + &mut self, + target_base: Interned, + 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, - ) { + ) -> 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,10 +1707,11 @@ impl AssertValidityState { ModuleBody::Normal(NormalModuleBody { body }) => { let body = self.make_block_index(body); assert_eq!(body, 0); - self.assert_subtree_validity(body); - for (base, state) in &self.target_states { - if base.must_connect_to() { - state.assert_written(); + if let Ok(()) = self.assert_subtree_validity(body) { + for (base, state) in &self.target_states { + if base.must_connect_to() { + state.assert_written(); + } } } } @@ -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" ), } diff --git a/crates/fayalite/src/module/transform/simplify_enums.rs b/crates/fayalite/src/module/transform/simplify_enums.rs index 8c99491..92d1668 100644 --- a/crates/fayalite/src/module/transform/simplify_enums.rs +++ b/crates/fayalite/src/module/transform/simplify_enums.rs @@ -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 for SimplifyEnumsError { + fn from(value: HitUndeducedType) -> Self { + Self::HitUndeducedType(value) + } +} + #[derive(Value, Clone, Eq, PartialEq, Hash, Debug)] struct TagAndBody { 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:: { 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(value: Option<&T>, target_ty: DynUIntType) -> DynUInt { +fn value_to_uint( + value: Option<&T>, + target_ty: DynUIntType, +) -> Result { 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, rhs: Expr, 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::(); 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::(); 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::>(); let rhs = rhs.with_type::>(); 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::>() - .body[..op.ty().bit_width()] + .body[..op.ty().bit_width()?] .cast_bits_to::(op.ty()) .expr_enum(), None => ().to_expr().expr_enum(), @@ -284,7 +298,7 @@ impl Folder for State { .with_type::(); 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::(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), }) diff --git a/crates/fayalite/src/module/transform/simplify_memories.rs b/crates/fayalite/src/module/transform/simplify_memories.rs index edcbb1b..aaaa90a 100644 --- a/crates/fayalite/src/module/transform/simplify_memories.rs +++ b/crates/fayalite/src/module/transform/simplify_memories.rs @@ -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 { + 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::>()?, }, 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| e.with_type::().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>, port_wdata: Option>, port_wmask: Option>, - ) { + ) -> Result<(), HitUndeducedType> { let mut input_array_types = vec![]; let connect_read = |output_stmts: &mut Vec, wire_read: Expr, @@ -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, 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>, output_stmts: &mut Vec, - ) { + ) -> 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(&mut self, v: Module) -> Result, 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>) -> Interned> { - module - .fold(&mut State::default()) - .unwrap_or_else(|v| match v {}) +pub fn simplify_memories( + module: Interned>, +) -> Result>, HitUndeducedType> { + module.fold(&mut State::default()) } diff --git a/crates/fayalite/src/module/transform/visit.rs b/crates/fayalite/src/module/transform/visit.rs index fcac723..6f2fa3d 100644 --- a/crates/fayalite/src/module/transform/visit.rs +++ b/crates/fayalite/src/module/transform/visit.rs @@ -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, }; diff --git a/crates/fayalite/src/reg.rs b/crates/fayalite/src/reg.rs index a3e152c..3f61692 100644 --- a/crates/fayalite/src/reg.rs +++ b/crates/fayalite/src/reg.rs @@ -106,7 +106,10 @@ impl Reg { clock_domain: Expr, init: Option>, ) -> 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"); } diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 58080ad..6e0f776 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -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 for AsyncResetType {} + impl Type for AsyncResetType { type Value = AsyncReset; type CanonicalType = AsyncResetType; @@ -91,8 +94,8 @@ impl Value for AsyncReset { fn to_canonical(&self) -> ::CanonicalValue { *self } - fn to_bits_impl(this: &Self) -> Interned { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, 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 { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { + Ok(interned_bit(this.0)) } } @@ -114,6 +117,8 @@ impl SyncResetType { } } +impl Connect for SyncResetType {} + impl Type for SyncResetType { type CanonicalType = SyncResetType; type Value = SyncReset; @@ -181,8 +186,8 @@ impl Value for SyncReset { fn to_canonical(&self) -> ::CanonicalValue { *self } - fn to_bits_impl(this: &Self) -> Interned { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, 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 { - interned_bit(this.0) + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { + Ok(interned_bit(this.0)) } } @@ -204,6 +209,8 @@ impl ResetType { } } +impl Connect for ResetType {} + impl Type for ResetType { type Value = Reset; type CanonicalType = ResetType; @@ -271,7 +278,7 @@ impl Value for Reset { fn to_canonical(&self) -> ::CanonicalValue { *self } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, 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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { match *this {} } } diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 1d7b7e9..a2cd913 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -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", CanonicalTypeKind::Reset => "Expr", CanonicalTypeKind::DynCanonicalType => "Expr", + CanonicalTypeKind::Deduce => "Expr", } } } @@ -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 { + 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 { + 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 { + 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 { + 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 { if let TypeEnum::BundleType(retval) = self { @@ -208,6 +216,13 @@ impl TypeEnum { None } } + pub fn undeduced(self) -> Option { + if let TypeEnum::Deduce(retval) = self { + Some(retval) + } else { + None + } + } pub fn to_dyn(self) -> Interned { 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 { @@ -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; 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; + fn is_storable(&self) -> Result; + fn is_castable_from_bits(&self) -> Result; + fn bit_width(&self) -> Result; fn as_dyn_type(&self) -> &dyn DynType; fn as_dyn_canonical_type(&self) -> Option<&dyn DynCanonicalType>; #[deprecated = "use ::downcast or ::downcast instead, they properly handle Interned and Interned"] @@ -294,19 +311,19 @@ impl DynType for T { self.type_enum() } - fn is_passive(&self) -> bool { + fn is_passive(&self) -> Result { self.type_enum().is_passive() } - fn is_storable(&self) -> bool { + fn is_storable(&self) -> Result { self.type_enum().is_storable() } - fn is_castable_from_bits(&self) -> bool { + fn is_castable_from_bits(&self) -> Result { self.type_enum().is_castable_from_bits() } - fn bit_width(&self) -> usize { + fn bit_width(&self) -> Result { self.type_enum().bit_width() } @@ -459,7 +476,9 @@ impl MatchVariantAndInactiveScope for MatchVariantWith } } -pub trait Type: DynType + Clone + Hash + Eq + Intern + Connect { +pub trait Type: + DynType + Clone + Hash + Eq + Intern + Connect + Connect +{ 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) -> Arc; - fn to_bits(&self) -> Interned; + fn to_bits(&self) -> Result, 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 { + fn to_bits_impl(this: &Self) -> Result, 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 { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { ::to_bits_impl(this) } } @@ -759,7 +780,7 @@ impl CanonicalValue for DynCanonicalValue { this.0.value_enum() } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { this.0.to_bits() } } @@ -807,7 +828,7 @@ pub trait Value: DynValueTrait + Clone + Eq + Hash + ToExpr { fn valueless(&self) -> Valueless { Valueless { ty: self.ty() } } - fn to_bits_impl(this: &Self) -> Interned { + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { <::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; + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType>; } impl DynValueTrait for T @@ -884,11 +905,13 @@ where self } - fn to_bits(&self) -> Interned { + fn to_bits(&self) -> Result, HitUndeducedType> { T::to_bits_impl(self) } } +impl Connect for Interned {} + impl Type for Interned { type CanonicalType = Interned; type Value = DynValue; @@ -977,6 +1000,8 @@ impl Type for Interned { impl Connect for Interned {} +impl Connect for Interned {} + impl sealed::Sealed for Interned {} impl CanonicalType for Interned { @@ -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 {} diff --git a/crates/fayalite/src/type_deduction.rs b/crates/fayalite/src/type_deduction.rs new file mode 100644 index 0000000..21a2411 --- /dev/null +++ b/crates/fayalite/src/type_deduction.rs @@ -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, r: Result) -> Result { + 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 Connect 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<::Value> { + match *self {} + } +} + +impl Value for Deduce { + fn to_canonical(&self) -> ::CanonicalValue { + *self + } +} + +impl CanonicalValue for Deduce { + fn value_enum_impl(this: &Self) -> ValueEnum { + match *this {} + } + + fn to_bits_impl(this: &Self) -> Result, HitUndeducedType> { + match *this {} + } +} diff --git a/crates/fayalite/visit_types.json b/crates/fayalite/visit_types.json index 5401097..40048f7 100644 --- a/crates/fayalite/visit_types.json +++ b/crates/fayalite/visit_types.json @@ -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"