use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{Intern, Interned},
source_location::SourceLocation,
ty::{
impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
TypeProperties, TypeWithDeref,
},
};
use hashbrown::HashMap;
use std::{fmt, marker::PhantomData};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct BundleField {
pub name: Interned<str>,
pub flipped: bool,
pub ty: CanonicalType,
}
impl BundleField {
pub fn fmt_debug_in_struct(self, field_offset: usize) -> FmtDebugInStruct {
FmtDebugInStruct {
field: self,
field_offset,
}
}
}
#[derive(Copy, Clone)]
pub struct FmtDebugInStruct {
field: BundleField,
field_offset: usize,
}
impl fmt::Debug for FmtDebugInStruct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
field: BundleField { name, flipped, ty },
field_offset,
} = *self;
if flipped {
write!(f, "#[hdl(flip)] ")?;
}
if f.alternate() {
writeln!(f, "/* offset = {field_offset} */")?;
}
write!(f, "{name}: ")?;
ty.fmt(f)
}
}
impl fmt::Display for FmtDebugInStruct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
#[derive(Clone, Eq)]
struct BundleImpl {
fields: Interned<[BundleField]>,
name_indexes: HashMap<Interned<str>, usize>,
field_offsets: Interned<[usize]>,
type_properties: TypeProperties,
}
impl std::hash::Hash for BundleImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.fields.hash(state);
}
}
impl PartialEq for BundleImpl {
fn eq(&self, other: &Self) -> bool {
self.fields == other.fields
}
}
impl std::fmt::Debug for BundleImpl {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Bundle ")?;
f.debug_set()
.entries(
self.fields
.iter()
.enumerate()
.map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
)
.finish()
}
}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct Bundle(Interned<BundleImpl>);
impl std::fmt::Debug for Bundle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone)]
pub struct BundleTypePropertiesBuilder(TypeProperties);
impl BundleTypePropertiesBuilder {
#[must_use]
pub const fn new() -> Self {
Self(TypeProperties {
is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: 0,
})
}
pub const fn clone(&self) -> Self {
Self(self.0)
}
#[must_use]
pub const fn field(self, flipped: bool, field_props: TypeProperties) -> Self {
let Some(bit_width) = self.0.bit_width.checked_add(field_props.bit_width) else {
panic!("bundle is too big: bit-width overflowed");
};
if flipped {
Self(TypeProperties {
is_passive: false,
is_storable: false,
is_castable_from_bits: false,
bit_width,
})
} else {
Self(TypeProperties {
is_passive: self.0.is_passive & field_props.is_passive,
is_storable: self.0.is_storable & field_props.is_storable,
is_castable_from_bits: self.0.is_castable_from_bits
& field_props.is_castable_from_bits,
bit_width,
})
}
}
pub const fn finish(self) -> TypeProperties {
self.0
}
}
impl Default for BundleTypePropertiesBuilder {
fn default() -> Self {
Self::new()
}
}
impl Bundle {
#[track_caller]
pub fn new(fields: Interned<[BundleField]>) -> Self {
let mut name_indexes = HashMap::with_capacity(fields.len());
let mut field_offsets = Vec::with_capacity(fields.len());
let mut type_props_builder = BundleTypePropertiesBuilder::new();
for (index, &BundleField { name, flipped, 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(type_props_builder.0.bit_width);
type_props_builder = type_props_builder.field(flipped, ty.type_properties());
}
Self(Intern::intern_sized(BundleImpl {
fields,
name_indexes,
field_offsets: Intern::intern_owned(field_offsets),
type_properties: type_props_builder.finish(),
}))
}
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes
}
pub fn field_by_name(&self, name: Interned<str>) -> Option<BundleField> {
Some(self.0.fields[*self.0.name_indexes.get(&name)?])
}
pub fn field_offsets(self) -> Interned<[usize]> {
self.0.field_offsets
}
pub fn type_properties(self) -> TypeProperties {
self.0.type_properties
}
pub fn can_connect(self, rhs: Self) -> bool {
if self.0.fields.len() != rhs.0.fields.len() {
return false;
}
for (
&BundleField {
name: lhs_name,
flipped: lhs_flipped,
ty: lhs_ty,
},
&BundleField {
name: rhs_name,
flipped: rhs_flipped,
ty: rhs_ty,
},
) in self.0.fields.iter().zip(rhs.0.fields.iter())
{
if lhs_name != rhs_name || lhs_flipped != rhs_flipped || !lhs_ty.can_connect(rhs_ty) {
return false;
}
}
true
}
}
impl Type for Bundle {
type BaseType = Bundle;
type MaskType = Bundle;
impl_match_variant_as_self!();
fn mask_type(&self) -> Self::MaskType {
Self::new(Interned::from_iter(self.0.fields.into_iter().map(
|BundleField { name, flipped, ty }| BundleField {
name,
flipped,
ty: ty.mask_type(),
},
)))
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Bundle(*self)
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Bundle(bundle) = canonical_type else {
panic!("expected bundle");
};
bundle
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
pub trait BundleType: Type<BaseType = Bundle> {
type Builder: Default;
type FilledBuilder: ToExpr<Type = Self>;
fn fields(&self) -> Interned<[BundleField]>;
}
#[derive(Default)]
pub struct NoBuilder;
pub struct Unfilled<T: Type>(PhantomData<T>);
impl<T: Type> Default for Unfilled<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl BundleType for Bundle {
type Builder = NoBuilder;
type FilledBuilder = Expr<Bundle>;
fn fields(&self) -> Interned<[BundleField]> {
self.0.fields
}
}
#[derive(Default)]
pub struct TupleBuilder<T>(T);
macro_rules! impl_tuple_builder_fields {
(
@impl
{
}
[
$({
#[type_var($head_type_var:ident)]
#[field($head_field:ident)]
#[var($head_var:ident)]
})*
]
{
#[type_var($cur_type_var:ident)]
#[field($cur_field:ident)]
#[var($cur_var:ident)]
}
[
$({
#[type_var($tail_type_var:ident)]
#[field($tail_field:ident)]
#[var($tail_var:ident)]
})*
]
) => {
impl<
$($head_type_var,)*
$cur_type_var: Type,
$($tail_type_var,)*
> TupleBuilder<(
$($head_type_var,)*
Unfilled<$cur_type_var>,
$($tail_type_var,)*
)>
{
pub fn $cur_field(self, $cur_var: impl ToExpr<Type = $cur_type_var>) -> TupleBuilder<(
$($head_type_var,)*
Expr<$cur_type_var>,
$($tail_type_var,)*
)>
{
let ($($head_var,)* _, $($tail_var,)*) = self.0;
TupleBuilder(($($head_var,)* $cur_var.to_expr(), $($tail_var,)*))
}
}
};
($global:tt [$($head:tt)*] $cur:tt [$next:tt $($tail:tt)*]) => {
impl_tuple_builder_fields!(@impl $global [$($head)*] $cur [$next $($tail)*]);
impl_tuple_builder_fields!($global [$($head)* $cur] $next [$($tail)*]);
};
($global:tt [$($head:tt)*] $cur:tt []) => {
impl_tuple_builder_fields!(@impl $global [$($head)*] $cur []);
};
($global:tt [$cur:tt $($tail:tt)*]) => {
impl_tuple_builder_fields!($global [] $cur [$($tail)*]);
};
($global:tt []) => {};
}
macro_rules! impl_tuples {
([$({#[num = $num:literal, field = $field:ident] $var:ident: $T:ident})*] []) => {
impl_tuple_builder_fields! {
{}
[$({
#[type_var($T)]
#[field($field)]
#[var($var)]
})*]
}
impl<$($T: Type,)*> Type for ($($T,)*) {
type BaseType = Bundle;
type MaskType = ($($T::MaskType,)*);
type MatchVariant = ($(Expr<$T>,)*);
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let _ = this;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*)))
}
fn mask_type(&self) -> Self::MaskType {
#![allow(clippy::unused_unit)]
let ($($var,)*) = self;
($($var.mask_type(),)*)
}
fn canonical(&self) -> CanonicalType {
Bundle::new(self.fields()).canonical()
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
#![allow(clippy::unused_unit)]
let CanonicalType::Bundle(bundle) = canonical_type else {
panic!("expected bundle");
};
let [$($var,)*] = *bundle.fields() else {
panic!("bundle has wrong number of fields");
};
$(let BundleField { name, flipped, ty } = $var;
assert_eq!(&*name, stringify!($num));
assert!(!flipped);
let $var = $T::from_canonical(ty);)*
($($var,)*)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
impl<$($T: Type,)*> BundleType for ($($T,)*) {
type Builder = TupleBuilder<($(Unfilled<$T>,)*)>;
type FilledBuilder = TupleBuilder<($(Expr<$T>,)*)>;
fn fields(&self) -> Interned<[BundleField]> {
let ($($var,)*) = self;
[$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
}
}
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
let _ = this;
Interned::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized())
}
}
impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
const TYPE: Self = ($($T::TYPE,)*);
const MASK_TYPE: Self::MaskType = ($($T::MASK_TYPE,)*);
const TYPE_PROPERTIES: TypeProperties = {
let builder = BundleTypePropertiesBuilder::new();
$(let builder = builder.field(false, $T::TYPE_PROPERTIES);)*
builder.finish()
};
const MASK_TYPE_PROPERTIES: TypeProperties = {
let builder = BundleTypePropertiesBuilder::new();
$(let builder = builder.field(false, $T::MASK_TYPE_PROPERTIES);)*
builder.finish()
};
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
type Type = ($($T::Type,)*);
fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_expr();)*
let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
}
}
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
type Type = ($($T,)*);
fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self.0;
let ty = ($(Expr::ty($var),)*);
let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values[..].intern()).to_expr()
}
}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]);
};
}
impl_tuples! {
[] [
{#[num = 0, field = field_0] v0: T0}
{#[num = 1, field = field_1] v1: T1}
{#[num = 2, field = field_2] v2: T2}
{#[num = 3, field = field_3] v3: T3}
{#[num = 4, field = field_4] v4: T4}
{#[num = 5, field = field_5] v5: T5}
{#[num = 6, field = field_6] v6: T6}
{#[num = 7, field = field_7] v7: T7}
{#[num = 8, field = field_8] v8: T8}
{#[num = 9, field = field_9] v9: T9}
{#[num = 10, field = field_10] v10: T10}
{#[num = 11, field = field_11] v11: T11}
]
}
impl<T: ?Sized + Send + Sync + 'static> Type for PhantomData<T> {
type BaseType = Bundle;
type MaskType = ();
type MatchVariant = PhantomData<T>;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
this: Expr<Self>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter {
let _ = this;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(PhantomData))
}
fn mask_type(&self) -> Self::MaskType {
()
}
fn canonical(&self) -> CanonicalType {
Bundle::new(self.fields()).canonical()
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Bundle(bundle) = canonical_type else {
panic!("expected bundle");
};
assert!(
bundle.fields().is_empty(),
"bundle has wrong number of fields"
);
PhantomData
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
pub struct PhantomDataBuilder<T: ?Sized + Send + Sync + 'static>(PhantomData<T>);
impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
fn default() -> Self {
Self(PhantomData)
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> {
PhantomData.to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> BundleType for PhantomData<T> {
type Builder = PhantomDataBuilder<T>;
type FilledBuilder = PhantomDataBuilder<T>;
fn fields(&self) -> Interned<[BundleField]> {
Interned::default()
}
}
impl<T: ?Sized + Send + Sync + 'static> TypeWithDeref for PhantomData<T> {
fn expr_deref(_this: &Expr<Self>) -> &Self::MatchVariant {
&PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
const TYPE: Self = PhantomData;
const MASK_TYPE: Self::MaskType = ();
const TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
type Type = PhantomData<T>;
fn to_expr(&self) -> Expr<Self::Type> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
}
}