WIP: use HdlOption[the_type_var] or UInt[123 + n] for creating types
All checks were successful
/ test (push) Successful in 14s

This commit is contained in:
Jacob Lifshay 2024-08-07 03:16:29 -07:00
parent cd99dbc849
commit 73d80cadf8
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
18 changed files with 2734 additions and 6895 deletions

View file

@ -7,13 +7,14 @@ jobs:
- uses: https://code.forgejo.org/actions/checkout@v3 - uses: https://code.forgejo.org/actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- run: | # FIXME: uncomment once the code works again
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0 # - run: |
source "$HOME/.cargo/env" # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
echo "$PATH" >> "$GITHUB_PATH" # source "$HOME/.cargo/env"
- uses: https://github.com/Swatinem/rust-cache@v2 # echo "$PATH" >> "$GITHUB_PATH"
with: # - uses: https://github.com/Swatinem/rust-cache@v2
save-if: ${{ github.ref == 'refs/heads/master' }} # with:
- run: cargo test # save-if: ${{ github.ref == 'refs/heads/master' }}
- run: cargo test --features=unstable-doc # - run: cargo test
- run: cargo doc --features=unstable-doc # - run: cargo test --features=unstable-doc
# - run: cargo doc --features=unstable-doc

View file

@ -0,0 +1,339 @@
use crate::{
hdl_type_common::{get_target, type_derives, TypeOptions, WrappedInConst},
kw, Errors, HdlAttr,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens};
use syn::{
parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace, Attribute, Field,
Fields, FieldsNamed, Generics, Ident, ItemStruct, Token, Visibility,
};
#[derive(Clone, Debug)]
pub struct ParsedBundle {
pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<TypeOptions>,
pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct],
pub(crate) ident: Ident,
pub(crate) generics: Generics,
pub(crate) fields: FieldsNamed,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>,
pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) match_variant_ident: Ident,
}
impl ParsedBundle {
fn parse_field(
errors: &mut Errors,
field: &mut Field,
index: usize,
) -> Option<HdlAttr<kw::flip>> {
let Field {
attrs,
vis: _,
mutability: _,
ident,
colon_token,
ty,
} = field;
let ident = ident.get_or_insert_with(|| format_ident!("_{index}", span = ty.span()));
colon_token.get_or_insert(Token![:](ident.span()));
let options = errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs));
options
}
fn parse(item: ItemStruct) -> syn::Result<Self> {
let ItemStruct {
mut attrs,
vis,
struct_token,
ident,
generics,
fields,
semi_token,
} = item;
let mut errors = Errors::new();
let options = errors
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
.unwrap_or_default();
let mut fields = match fields {
syn::Fields::Named(fields) => fields,
syn::Fields::Unnamed(fields) => {
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
FieldsNamed {
brace_token: Brace(fields.paren_token.span),
named: fields.unnamed,
}
}
syn::Fields::Unit => {
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
FieldsNamed {
brace_token: Brace(semi_token.unwrap_or_default().span),
named: Punctuated::default(),
}
}
};
let mut field_flips = Vec::with_capacity(fields.named.len());
for (index, field) in fields.named.iter_mut().enumerate() {
field_flips.push(Self::parse_field(&mut errors, field, index));
}
errors.finish()?;
Ok(Self {
attrs,
options,
vis,
struct_token,
generics,
fields,
field_flips,
mask_type_ident: format_ident!("__{ident}__MaskType"),
mask_type_match_variant_ident: format_ident!("__{ident}__MaskType__MatchVariant"),
match_variant_ident: format_ident!("__{ident}__MatchVariant"),
ident,
})
}
}
impl ToTokens for ParsedBundle {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self {
attrs,
options,
vis,
struct_token,
ident,
generics,
fields,
field_flips,
mask_type_ident,
mask_type_match_variant_ident,
match_variant_ident,
} = self;
let TypeOptions {
outline_generated: _,
connect_inexact,
target,
} = &options.body;
let target = get_target(target, ident);
let mut item_attrs = attrs.clone();
item_attrs.push(type_derives(ident.span()));
ItemStruct {
attrs: item_attrs,
vis: vis.clone(),
struct_token: *struct_token,
ident: ident.clone(),
generics: generics.clone(),
fields: Fields::Named(fields.clone()),
semi_token: None,
}
.to_tokens(tokens);
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
let mut wrapped_in_const = WrappedInConst::new(tokens, ident.span());
let tokens = wrapped_in_const.inner();
let mut mask_type_fields = fields.clone();
for Field { ident, ty, .. } in &mut mask_type_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
<#ty as ::fayalite::ty::Type>::MaskType
};
}
ItemStruct {
attrs: vec![
parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types)]
},
type_derives(ident.span()),
],
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_ident.clone(),
generics: generics.clone(),
fields: Fields::Named(mask_type_fields.clone()),
semi_token: None,
}
.to_tokens(tokens);
let mut mask_type_match_variant_fields = mask_type_fields;
for Field { ident, ty, .. } in &mut mask_type_match_variant_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
attrs: vec![parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types)]
}],
vis: vis.clone(),
struct_token: *struct_token,
ident: mask_type_match_variant_ident.clone(),
generics: generics.clone(),
fields: Fields::Named(mask_type_match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
let mut match_variant_fields = fields.clone();
for Field { ident, ty, .. } in &mut match_variant_fields.named {
let ident = ident.as_ref().unwrap();
*ty = parse_quote_spanned! {ident.span()=>
::fayalite::expr::Expr<#ty>
};
}
ItemStruct {
attrs: vec![parse_quote_spanned! {ident.span()=>
#[allow(non_camel_case_types)]
}],
vis: vis.clone(),
struct_token: *struct_token,
ident: match_variant_ident.clone(),
generics: generics.clone(),
fields: Fields::Named(match_variant_fields),
semi_token: None,
}
.to_tokens(tokens);
let match_variant_body_fields =
Vec::from_iter(fields.named.iter().map(|Field { ident, .. }| {
let ident: &Ident = ident.as_ref().unwrap();
let ident_str = ident.to_string();
quote_spanned! {ident.span()=>
#ident: ::fayalite::expr::Expr::field(this, #ident_str),
}
}));
let mask_type_body_fields =
Vec::from_iter(fields.named.iter().map(|Field { ident, .. }| {
let ident: &Ident = ident.as_ref().unwrap();
quote_spanned! {ident.span()=>
#ident: ::fayalite::ty::Type::mask_type(&self.#ident),
}
}));
let from_canonical_body_fields = Vec::from_iter(fields.named.iter().enumerate().zip(field_flips).map(
|((index, Field { ident, .. }), flip)| {
let ident: &Ident = ident.as_ref().unwrap();
let ident_str = ident.to_string();
let flipped = flip.is_some();
quote_spanned! {ident.span()=>
#ident: {
let ::fayalite::bundle::BundleField { name, flipped, ty } = fields[#index];
::fayalite::__std::assert_eq!(&*name, #ident_str);
::fayalite::__std::assert_eq!(flipped, #flipped);
::fayalite::ty::Type::from_canonical(ty)
},
}
},
));
let fields_len = fields.named.len();
quote_spanned! {ident.span()=>
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
#where_clause
{
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #mask_type_match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
this: ::fayalite::expr::Expr<Self>,
module_builder: &mut ::fayalite::module::ModuleBuilder<
::fayalite::module::NormalModule,
>,
source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let _ = this;
let _ = module_builder;
let _ = source_location;
let retval = #mask_type_match_variant_ident {
#(#match_variant_body_fields)*
};
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(retval))
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
*self
}
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
}
#[track_caller]
fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self {
let ::fayalite::ty::CanonicalType::Bundle(bundle) = canonical_type else {
::fayalite::__std::panic!("expected bundle");
};
let fields = ::fayalite::bundle::BundleType::fields(&bundle);
::fayalite::__std::assert_eq!(fields.len(), #fields_len, "bundle has wrong number of fields");
Self {
#(#from_canonical_body_fields)*
}
}
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
#where_clause
{
type MaskType = #mask_type_ident #type_generics;
type MatchVariant = #match_variant_ident #type_generics;
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<
<Self as ::fayalite::ty::Type>::MatchVariant,
>;
type MatchVariantsIter = ::fayalite::__std::iter::Once<
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
>;
fn match_variants(
this: ::fayalite::expr::Expr<Self>,
module_builder: &mut ::fayalite::module::ModuleBuilder<
::fayalite::module::NormalModule,
>,
source_location: ::fayalite::source_location::SourceLocation,
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
let _ = this;
let _ = module_builder;
let _ = source_location;
let retval = #mask_type_match_variant_ident {
#(#match_variant_body_fields)*
};
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(retval))
}
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
#mask_type_ident {
#(#mask_type_body_fields)*
}
}
fn canonical(&self) -> ::fayalite::ty::CanonicalType {
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(self)))
}
#[track_caller]
fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self {
let ::fayalite::ty::CanonicalType::Bundle(bundle) = canonical_type else {
::fayalite::__std::panic!("expected bundle");
};
let fields = ::fayalite::bundle::BundleType::fields(&bundle);
::fayalite::__std::assert_eq!(fields.len(), #fields_len, "bundle has wrong number of fields");
Self {
#(#from_canonical_body_fields)*
}
}
fn source_location() -> ::fayalite::source_location::SourceLocation {
::fayalite::source_location::SourceLocation::caller()
}
}
}
.to_tokens(tokens);
}
}
pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> {
let item = ParsedBundle::parse(item)?;
let outline_generated = item.options.body.outline_generated;
let mut contents = item.to_token_stream();
if outline_generated.is_some() {
contents = crate::outline_generated(contents, "hdl-bundle-");
}
Ok(contents)
}

View file

@ -0,0 +1,6 @@
use proc_macro2::TokenStream;
use syn::ItemEnum;
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
todo!()
}

View file

@ -0,0 +1,70 @@
use crate::{fold::impl_fold, kw};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned, ToTokens};
use syn::{
parse::{Parse, ParseStream},
parse_quote_spanned,
punctuated::Punctuated,
token::Paren,
Attribute, Ident, Path, Token, WhereClause, WherePredicate,
};
crate::options! {
#[options = TypeOptions]
pub(crate) enum TypeOption {
OutlineGenerated(outline_generated),
ConnectInexact(connect_inexact),
Target(target, Path),
}
}
pub(crate) struct WrappedInConst<'a> {
outer: &'a mut TokenStream,
span: Span,
inner: TokenStream,
}
impl<'a> WrappedInConst<'a> {
pub(crate) fn new(outer: &'a mut TokenStream, span: Span) -> Self {
Self {
outer,
span,
inner: TokenStream::new(),
}
}
pub(crate) fn inner(&mut self) -> &mut TokenStream {
&mut self.inner
}
}
impl Drop for WrappedInConst<'_> {
fn drop(&mut self) {
let inner = &self.inner;
quote_spanned! {self.span=>
const _: () = {
#inner
};
}
.to_tokens(self.outer);
}
}
pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path {
match target {
Some((_, _, target)) => target.clone(),
None => item_ident.clone().into(),
}
}
pub(crate) fn type_derives(span: Span) -> Attribute {
parse_quote_spanned! {span=>
#[::fayalite::__std::prelude::v1::derive(
::fayalite::__std::fmt::Debug,
::fayalite::__std::cmp::Eq,
::fayalite::__std::cmp::PartialEq,
::fayalite::__std::hash::Hash,
::fayalite::__std::marker::Copy,
::fayalite::__std::clone::Clone,
)]
}
}

View file

@ -13,6 +13,9 @@ use syn::{
}; };
mod fold; mod fold;
mod hdl_bundle;
mod hdl_enum;
mod hdl_type_common;
mod module; mod module;
mod value_derive_common; mod value_derive_common;
mod value_derive_enum; mod value_derive_enum;
@ -728,3 +731,15 @@ pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
)), )),
} }
} }
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?;
match item {
Item::Enum(item) => hdl_enum::hdl_enum(item),
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
_ => Err(syn::Error::new(
Span::call_site(),
"top-level #[hdl] can only be used on structs or enums",
)),
}
}

View file

@ -1290,7 +1290,10 @@ impl Visitor {
memory, memory,
paren, paren,
ty_expr, ty_expr,
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())), } => (
paren,
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
),
MemoryFn::MemoryArray { MemoryFn::MemoryArray {
memory_array, memory_array,
paren, paren,

View file

@ -24,3 +24,15 @@ pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
Err(err) => err.into_compile_error().into(), Err(err) => err.into_compile_error().into(),
} }
} }
// intentionally not documented here, see `fayalite::hdl` for docs
#[proc_macro_attribute]
pub fn hdl(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) {
Ok(retval) => retval.into(),
Err(err) => err.into_compile_error().into(),
}
}

View file

@ -1,671 +1,185 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
bundle::{BundleType, BundleValue}, expr::{ops::ArrayIndex, Expr, ToExpr},
expr::{ int::{DynSize, KnownSize, Size},
ops::{ArrayIndex, ArrayLiteral, ExprIndex}, intern::{Intern, Interned},
Expr, ToExpr, module::{ModuleBuilder, NormalModule},
},
intern::{Intern, Interned, InternedCompare, Memoize},
module::{
transform::visit::{Fold, Folder, Visit, Visitor},
ModuleBuilder, NormalModule,
},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
StaticValue, Type, TypeEnum, Value, ValueEnum,
}, },
util::{ConstBool, GenericConstBool, MakeMutSlice}, util::ConstUsize,
}; };
use bitvec::{slice::BitSlice, vec::BitVec}; use std::ops::Index;
use std::{
any::Any, #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
borrow::{Borrow, BorrowMut}, struct ArrayImpl<T: Type, Len: Size> {
fmt, element: T,
hash::Hash, len: Len::SizeType,
marker::PhantomData, type_properties: TypeProperties,
ops::IndexMut, }
sync::Arc,
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
impl_: Interned<ArrayImpl<T, Len>>,
}
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ArrayImpl {
element, len: _, ..
} = *self.impl_;
write!(f, "Array<{element:?}, {}>", self.len())
}
}
pub type Array<
T = CanonicalType,
const LEN: usize = { <DynSize as crate::util::GenericConstUsize>::VALUE },
> = ArrayType<T, ConstUsize<LEN>>;
#[allow(non_upper_case_globals)]
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
impl<T: Type, Len: Size> ArrayType<T, Len> {
pub fn new(element: T, len: Len::SizeType) -> Self {
let element_props = element.canonical().type_properties();
let type_properties = TypeProperties {
is_passive: element_props.is_passive,
is_storable: element_props.is_storable,
is_castable_from_bits: element_props.is_castable_from_bits,
bit_width: element_props
.bit_width
.checked_mul(Len::as_usize(len))
.expect("array too big"),
}; };
mod sealed {
pub trait Sealed {}
}
pub trait ValueArrayOrSlice:
sealed::Sealed
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
+ Hash
+ fmt::Debug
+ Eq
+ Send
+ Sync
+ 'static
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
+ ToOwned
+ InternedCompare
{
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
type Match: 'static
+ Clone
+ Eq
+ fmt::Debug
+ Hash
+ Send
+ Sync
+ BorrowMut<[Expr<Self::Element>]>;
type MaskVA: ValueArrayOrSlice<
Element = <Self::ElementType as Type>::MaskValue,
ElementType = <Self::ElementType as Type>::MaskType,
LenType = Self::LenType,
MaskVA = Self::MaskVA,
> + ?Sized;
type IsStaticLen: GenericConstBool;
const FIXED_LEN_TYPE: Option<Self::LenType>;
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
fn len_from_len_type(v: Self::LenType) -> usize;
#[allow(clippy::result_unit_err)]
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
fn len_type(&self) -> Self::LenType;
fn len(&self) -> usize;
fn is_empty(&self) -> bool;
fn iter(&self) -> std::slice::Iter<Self::Element> {
Borrow::<[_]>::borrow(self).iter()
}
fn clone_to_arc(&self) -> Arc<Self>;
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
}
impl<T> sealed::Sealed for [T] {}
impl<V: Value> ValueArrayOrSlice for [V]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = usize;
type Match = Box<[Expr<V>]>;
type MaskVA = [<Self::ElementType as Type>::MaskValue];
type IsStaticLen = ConstBool<false>;
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
(0..array.canonical_type().len())
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
.collect()
}
fn len_from_len_type(v: Self::LenType) -> usize {
v
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
Ok(v)
}
fn len_type(&self) -> Self::LenType {
self.len()
}
fn len(&self) -> usize {
<[_]>::len(self)
}
fn is_empty(&self) -> bool {
<[_]>::is_empty(self)
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::from(self)
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
MakeMutSlice::make_mut_slice(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
impl<T, const N: usize> sealed::Sealed for [T; N] {}
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
where
V::Type: Type<Value = V>,
{
type Element = V;
type ElementType = V::Type;
type LenType = ();
type Match = [Expr<V>; N];
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
type IsStaticLen = ConstBool<true>;
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
std::array::from_fn(|index| {
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
})
}
fn len_from_len_type(_v: Self::LenType) -> usize {
N
}
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
if v == N {
Ok(())
} else {
Err(())
}
}
fn len_type(&self) -> Self::LenType {}
fn len(&self) -> usize {
N
}
fn is_empty(&self) -> bool {
N == 0
}
fn clone_to_arc(&self) -> Arc<Self> {
Arc::new(self.clone())
}
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
Arc::make_mut(v)
}
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
self
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
element: VA::ElementType,
len: VA::LenType,
bit_width: usize,
}
pub trait ArrayTypeTrait:
Type<
CanonicalType = ArrayType<[DynCanonicalValue]>,
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
CanonicalValue = Array<[DynCanonicalValue]>,
MaskType = ArrayType<
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
>,
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
+ sealed::Sealed
+ Connect<Self>
{
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
+ ?Sized;
type Element: Value<Type = Self::ElementType>;
type ElementType: Type<Value = Self::Element>;
}
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
type ValueArrayOrSlice = VA;
type Element = VA::Element;
type ElementType = VA::ElementType;
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
fn clone(&self) -> Self {
Self { Self {
element: self.element.clone(), impl_: ArrayImpl {
len: self.len,
bit_width: self.bit_width,
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
pub fn element(&self) -> &VA::ElementType {
&self.element
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.len)
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn bit_width(&self) -> usize {
self.bit_width
}
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
ArrayType {
len: self.len(),
element: self.element,
bit_width: self.bit_width,
}
}
#[track_caller]
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
Self::new_with_len_type(
element,
VA::try_len_type_from_len(len).expect("length should match"),
)
}
#[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 {
panic!("array is too big: bit-width overflowed");
};
ArrayType {
element, element,
len, len,
bit_width, type_properties,
}
.intern_sized(),
} }
} }
pub fn element(&self) -> &T {
&self.impl_.element
} }
pub fn len(self) -> usize {
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA> Len::as_usize(self.impl_.len)
where
VA::ElementType: Fold<State>,
{
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
state.fold_array_type(self)
} }
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> { pub fn type_properties(self) -> TypeProperties {
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len)) self.impl_.type_properties
}
pub fn as_dyn_array(self) -> Array {
Array::new_dyn(self.element().canonical(), self.len())
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA> impl<T: Type, Len: KnownSize> ArrayType<T, Len> {
where pub fn new_static(element: T) -> Self {
VA::ElementType: Visit<State>, Self::new(element, Len::SizeType::default())
{
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
state.visit_array_type(self)
}
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
self.element.visit(state)
} }
} }
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> { impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
pub fn new_array(element: V::Type) -> Self {
ArrayType::new_with_len_type(element, ())
}
}
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
fn static_type() -> Self { fn static_type() -> Self {
Self::new_array(StaticType::static_type()) Self::new_static(T::static_type())
} }
} }
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> { impl<T: Type> Array<T> {
pub fn new_slice(element: V::Type, len: usize) -> Self { pub fn new_dyn(element: T, len: usize) -> Self {
ArrayType::new_with_len_type(element, len) Self::new(element, len)
} }
} }
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> { impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
type CanonicalType = ArrayType<[DynCanonicalValue]>; type MaskType = ArrayType<T::MaskType, Len>;
type Value = Array<VA>; type MatchVariant = Len::ArrayMatch<T>;
type CanonicalValue = Array<[DynCanonicalValue]>;
type MaskType = ArrayType<VA::MaskVA>;
type MaskValue = Array<VA::MaskVA>;
type MatchVariant = VA::Match;
type MatchActiveScope = (); type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>; type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>( fn match_variants(
this: Expr<Self::Value>, this: Expr<Self>,
module_builder: &mut ModuleBuilder<IO, NormalModule>, module_builder: &mut ModuleBuilder<NormalModule>,
source_location: SourceLocation, source_location: SourceLocation,
) -> Self::MatchVariantsIter ) -> Self::MatchVariantsIter {
where let base = Expr::as_dyn_array(this);
IO::Type: BundleType<Value = IO>, let base_ty = Expr::ty(base);
{
let _ = module_builder; let _ = module_builder;
let _ = source_location; let _ = source_location;
std::iter::once(MatchVariantWithoutScope(VA::make_match(this))) let retval = Vec::from_iter(
(0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()),
);
std::iter::once(MatchVariantWithoutScope(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
} }
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
#[derive(Clone, Hash, Eq, PartialEq)] ArrayType::new(self.element().mask_type(), self.impl_.len)
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
type Input = ArrayType<T::ValueArrayOrSlice>;
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
fn inner(self, input: &Self::Input) -> Self::Output {
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
}
}
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
} }
fn canonical(&self) -> Self::CanonicalType { fn canonical(&self) -> CanonicalType {
ArrayType { CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
element: self.element.canonical_dyn(),
len: self.len(),
bit_width: self.bit_width,
}
} }
fn source_location(&self) -> SourceLocation { #[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Array(array) = canonical_type else {
panic!("expected array");
};
Self::new(
T::from_canonical(*array.element()),
Len::from_usize(array.len()),
)
}
fn source_location() -> SourceLocation {
SourceLocation::builtin() SourceLocation::builtin()
} }
fn type_enum(&self) -> TypeEnum {
TypeEnum::ArrayType(self.canonical())
} }
fn from_canonical_type(t: Self::CanonicalType) -> Self { impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
Self { fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
element: VA::ElementType::from_dyn_canonical_type(t.element), let base = Expr::as_dyn_array(*this);
len: VA::try_len_type_from_len(t.len).expect("length should match"), let base_ty = Expr::ty(base);
bit_width: t.bit_width, let retval = Vec::from_iter(
(0..base_ty.len()).map(|i| ArrayIndex::new_unchecked(base, i).to_expr()),
);
Interned::<_>::into_inner(Intern::intern_sized(
Len::ArrayMatch::<T>::try_from(retval)
.ok()
.expect("unreachable"),
))
} }
} }
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>( pub struct ArrayWithoutGenerics;
this,
)?) impl<T: Type> Index<T> for ArrayWithoutGenerics {
type Output = ArrayWithoutLen<T>;
fn index(&self, element: T) -> &Self::Output {
Interned::<_>::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
} }
} }
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>> #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
for ArrayType<Lhs> pub struct ArrayWithoutLen<T: Type> {
{ element: T,
} }
impl CanonicalType for ArrayType<[DynCanonicalValue]> { impl<T: Type> Index<usize> for ArrayWithoutLen<T> {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType; type Output = Array<T>;
}
#[derive(Debug, PartialEq, Eq, Hash)] fn index(&self, len: usize) -> &Self::Output {
pub struct Array<VA: ValueArrayOrSlice + ?Sized> { Interned::<_>::into_inner(Intern::intern_sized(Array::new_dyn(self.element, len)))
element_ty: VA::ElementType,
value: Arc<VA>,
}
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
fn clone(&self) -> Self {
Self {
element_ty: self.element_ty.clone(),
value: self.value.clone(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
type Type = ArrayType<VA>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
Array {
element_ty: self.element_ty.canonical_dyn(),
value: AsRef::<[_]>::as_ref(&*self.value)
.iter()
.map(|v| v.to_canonical_dyn())
.collect(),
}
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
fn clone(&self) -> Self {
*self
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
type Input = Array<VA>;
type InputOwned = Array<VA>;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let mut bits = BitVec::with_capacity(input.ty().bit_width());
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
bits.extend_from_bitslice(&element.to_bits());
}
Intern::intern_owned(bits)
}
}
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
}
}
impl CanonicalValue for Array<[DynCanonicalValue]> {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Array(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
Value::to_bits_impl(this)
}
}
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
pub fn element_ty(&self) -> &VA::ElementType {
&self.element_ty
}
pub fn len(&self) -> usize {
VA::len_from_len_type(self.value.len_type())
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn value(&self) -> &Arc<VA> {
&self.value
}
pub fn set_element(&mut self, index: usize, element: VA::Element) {
assert_eq!(self.element_ty, element.ty());
VA::arc_make_mut(&mut self.value)[index] = element;
}
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
for element in value.iter() {
assert_eq!(element_ty, element.ty());
}
Self { element_ty, value }
}
pub fn into_slice(self) -> Array<[VA::Element]> {
Array {
element_ty: self.element_ty,
value: self.value.arc_to_arc_slice(),
}
}
}
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
where
VA::Element: StaticValue,
{
fn from(value: T) -> Self {
Self::new(StaticType::static_type(), value.into())
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
type Type = ArrayType<[T::Value]>;
fn ty(&self) -> Self::Type {
<[E]>::ty(self)
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
<[E]>::to_expr(self)
}
}
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
type Type = ArrayType<[T::Value; N]>;
fn ty(&self) -> Self::Type {
ArrayType::new_with_len_type(StaticType::static_type(), ())
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let elements = Intern::intern_owned(Vec::from_iter(
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
));
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
}
}
#[derive(Clone, Debug)]
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
array: Arc<VA>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
type Item = VA::Element;
fn next(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next()?].clone())
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(self.array[self.indexes.next_back()?].clone())
}
}
impl<VA: ValueArrayOrSlice> Array<VA> {
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
self.value.iter()
}
}
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
type Item = &'a VA::Element;
type IntoIter = std::slice::Iter<'a, VA::Element>;
fn into_iter(self) -> Self::IntoIter {
self.value.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
type Item = VA::Element;
type IntoIter = ArrayIntoIter<VA>;
fn into_iter(self) -> Self::IntoIter {
ArrayIntoIter {
indexes: 0..self.len(),
array: self.value,
}
}
}
#[derive(Clone, Debug)]
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
array: Expr<Array<VA>>,
indexes: std::ops::Range<usize>,
}
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
type Item = Expr<VA::Element>;
fn next(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.indexes.size_hint()
}
}
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
fn next_back(&mut self) -> Option<Self::Item> {
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
type Item = Expr<VA::Element>;
type IntoIter = ArrayExprIter<VA>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
pub fn len(self) -> usize {
self.canonical_type().len()
}
pub fn is_empty(self) -> bool {
self.canonical_type().is_empty()
}
pub fn iter(self) -> ArrayExprIter<VA> {
ArrayExprIter {
indexes: 0..self.len(),
array: self,
}
} }
} }

View file

@ -1,806 +1,253 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr}, expr::{ops::BundleLiteral, Expr, ToExpr},
intern::{ intern::{Intern, Interned},
Intern, Interned, InternedCompare, Memoize, PtrEqWithTypeId, SupportsPtrEqWithTypeId,
},
module::{ModuleBuilder, NormalModule}, module::{ModuleBuilder, NormalModule},
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType, impl_match_variant_as_self, CanonicalType, MatchVariantWithoutScope, StaticType, Type,
DynCanonicalValue, DynType, MatchVariantWithoutScope, StaticType, Type, TypeEnum, TypeProperties, TypeWithDeref,
TypeWithDeref, Value, ValueEnum,
}, },
}; };
use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
sync::Arc,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct FieldType<T> { pub struct BundleField {
pub name: Interned<str>, pub name: Interned<str>,
pub flipped: bool, pub flipped: bool,
pub ty: T, pub ty: CanonicalType,
}
pub struct FmtDebugInStruct<'a, T> {
field: &'a FieldType<T>,
field_offset: usize,
}
impl<T: fmt::Debug> fmt::Debug for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
field:
&FieldType {
name,
flipped,
ref 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<T: fmt::Debug> fmt::Display for FmtDebugInStruct<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> FieldType<T> {
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> FieldType<U> {
let Self { name, flipped, ty } = self;
FieldType {
name,
flipped,
ty: f(ty),
}
}
pub fn as_ref_ty(&self) -> FieldType<&T> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: &self.ty,
}
}
pub fn fmt_debug_in_struct(&self, field_offset: usize) -> FmtDebugInStruct<'_, T> {
FmtDebugInStruct {
field: self,
field_offset,
}
}
}
impl<T: Type> FieldType<T> {
pub fn canonical(&self) -> FieldType<T::CanonicalType> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical(),
}
}
pub fn to_dyn(&self) -> FieldType<Interned<dyn DynType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.to_dyn(),
}
}
pub fn canonical_dyn(&self) -> FieldType<Interned<dyn DynCanonicalType>> {
FieldType {
name: self.name,
flipped: self.flipped,
ty: self.ty.canonical_dyn(),
}
}
}
impl FieldType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper<T: Type>(
self,
expected_name: &str,
expected_flipped: bool,
) -> T {
assert_eq!(&*self.name, expected_name, "field name doesn't match");
assert_eq!(
self.flipped, expected_flipped,
"field {expected_name} orientation (flipped or not) doesn't match"
);
let ty = &*self.ty;
if let Ok(ty) = <dyn DynCanonicalType>::downcast(ty) {
return T::from_canonical_type(ty);
}
let type_name = std::any::type_name::<T::CanonicalType>();
panic!("field {expected_name} type doesn't match, expected: {type_name:?}, got: {ty:?}");
}
} }
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
struct DynBundleTypeImpl { struct BundleImpl {
fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>, fields: Interned<[BundleField]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
field_offsets: Interned<[usize]>, field_offsets: Interned<[usize]>,
is_passive: bool, type_properties: TypeProperties,
is_storable: bool,
is_castable_from_bits: bool,
bit_width: usize,
} }
impl fmt::Debug for DynBundleTypeImpl { impl std::hash::Hash for BundleImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
write!(f, "DynBundleType ")?; self.fields.hash(state);
f.debug_set()
.entries(
self.fields
.iter()
.enumerate()
.map(|(index, field)| field.fmt_debug_in_struct(self.field_offsets[index])),
)
.finish()
} }
} }
impl PartialEq for DynBundleTypeImpl { impl PartialEq for BundleImpl {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.fields == other.fields self.fields == other.fields
} }
} }
impl Hash for DynBundleTypeImpl { impl std::fmt::Debug for BundleImpl {
fn hash<H: Hasher>(&self, state: &mut H) { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fields.hash(state); f.debug_struct("Bundle")
.field("fields", &self.fields)
.field("name_indexes", &self.name_indexes)
.field("field_offsets", &self.field_offsets)
.field("type_properties", &self.type_properties)
.finish()
} }
} }
#[derive(Copy, Clone, Hash, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct DynBundleType(Interned<DynBundleTypeImpl>); pub struct Bundle(Interned<BundleImpl>);
impl fmt::Debug for DynBundleType { impl std::fmt::Debug for Bundle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) self.0.fmt(f)
} }
} }
impl DynBundleType { impl Bundle {
pub fn new(fields: Interned<[FieldType<Interned<dyn DynCanonicalType>>]>) -> Self { #[track_caller]
let is_passive = fields pub fn new(fields: Interned<[BundleField]>) -> Self {
.iter()
.all(|field| !field.flipped && field.ty.is_passive());
let is_storable = fields
.iter()
.all(|field| !field.flipped && field.ty.is_storable());
let is_castable_from_bits = fields
.iter()
.all(|field| !field.flipped && field.ty.is_castable_from_bits());
let mut name_indexes = HashMap::with_capacity(fields.len()); let mut name_indexes = HashMap::with_capacity(fields.len());
let mut field_offsets = Vec::with_capacity(fields.len()); let mut field_offsets = Vec::with_capacity(fields.len());
let mut bit_width = 0usize; let mut type_properties = TypeProperties {
for (index, &FieldType { name, ty, .. }) in fields.iter().enumerate() { is_passive: true,
is_storable: true,
is_castable_from_bits: true,
bit_width: 0,
};
for (index, &BundleField { name, flipped, ty }) in fields.iter().enumerate() {
if let Some(old_index) = name_indexes.insert(name, index) { if let Some(old_index) = name_indexes.insert(name, index) {
panic!("duplicate field name {name:?}: at both index {old_index} and {index}"); panic!("duplicate field name {name:?}: at both index {old_index} and {index}");
} }
field_offsets.push(bit_width); field_offsets.push(type_properties.bit_width);
bit_width = bit_width let bit_width = type_properties
.bit_width
.checked_add(ty.bit_width()) .checked_add(ty.bit_width())
.unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed")); .unwrap_or_else(|| panic!("bundle is too big: bit-width overflowed"));
type_properties = if flipped {
TypeProperties {
is_passive: false,
is_storable: false,
is_castable_from_bits: false,
bit_width,
} }
Self( } else {
DynBundleTypeImpl { let field_props = ty.type_properties();
TypeProperties {
is_passive: type_properties.is_passive & field_props.is_passive,
is_storable: type_properties.is_storable & field_props.is_storable,
is_castable_from_bits: type_properties.is_castable_from_bits
& field_props.is_castable_from_bits,
bit_width,
}
};
}
Self(Intern::intern_sized(BundleImpl {
fields, fields,
name_indexes, name_indexes,
field_offsets: Intern::intern_owned(field_offsets), field_offsets: Intern::intern_owned(field_offsets),
is_passive, type_properties,
is_storable, }))
is_castable_from_bits,
bit_width,
}
.intern_sized(),
)
}
pub fn is_passive(self) -> bool {
self.0.is_passive
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
} }
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes &self.0.name_indexes
} }
pub fn field_by_name( pub fn field_by_name(&self, name: Interned<str>) -> Option<BundleField> {
&self,
name: Interned<str>,
) -> Option<FieldType<Interned<dyn DynCanonicalType>>> {
Some(self.0.fields[*self.0.name_indexes.get(&name)?]) Some(self.0.fields[*self.0.name_indexes.get(&name)?])
} }
pub fn field_offsets(self) -> Interned<[usize]> { pub fn field_offsets(self) -> Interned<[usize]> {
self.0.field_offsets self.0.field_offsets
} }
} pub fn type_properties(self) -> TypeProperties {
self.0.type_properties
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynBundle {
ty: DynBundleType,
fields: Arc<[DynCanonicalValue]>,
}
impl DynBundle {
pub fn new(ty: DynBundleType, fields: Arc<[DynCanonicalValue]>) -> Self {
assert_eq!(
ty.fields().len(),
fields.len(),
"field values don't match type"
);
for (field_ty, field) in ty.fields().iter().zip(fields.iter()) {
assert_eq!(field_ty.ty, field.ty(), "field value doesn't match type");
}
DynBundle { ty, fields }
}
pub fn fields(&self) -> &Arc<[DynCanonicalValue]> {
&self.fields
} }
} }
pub trait TypeHintTrait: Send + Sync + fmt::Debug + SupportsPtrEqWithTypeId { impl Type for Bundle {
fn matches(&self, ty: &dyn DynType) -> Result<(), String>; 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 {
impl InternedCompare for dyn TypeHintTrait { CanonicalType::Bundle(*self)
type InternedCompareKey = PtrEqWithTypeId;
fn interned_compare_key_ref(this: &Self) -> Self::InternedCompareKey {
Self::get_ptr_eq_with_type_id(this)
} }
fn interned_compare_key_weak(this: &std::sync::Weak<Self>) -> Self::InternedCompareKey { #[track_caller]
Self::get_ptr_eq_with_type_id(&*this.upgrade().unwrap()) fn from_canonical(canonical_type: CanonicalType) -> Self {
} let CanonicalType::Bundle(bundle) = canonical_type else {
} panic!("expected bundle");
pub struct TypeHint<T: Type>(PhantomData<fn(T)>);
impl<T: Type> TypeHint<T> {
pub fn intern_dyn() -> Interned<dyn TypeHintTrait> {
Interned::cast_unchecked(
Self(PhantomData).intern_sized(),
|v| -> &dyn TypeHintTrait { v },
)
}
}
impl<T: Type> fmt::Debug for TypeHint<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "TypeHint<{}>", std::any::type_name::<T>())
}
}
impl<T: Type + Hash> Hash for TypeHint<T> {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl<T: Type> Eq for TypeHint<T> {}
impl<T: Type> PartialEq for TypeHint<T> {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl<T: Type> Clone for TypeHint<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: Type> Copy for TypeHint<T> {}
impl<T: Type> TypeHintTrait for TypeHint<T> {
fn matches(&self, ty: &dyn DynType) -> Result<(), String> {
match ty.downcast::<T>() {
Ok(_) => Ok(()),
Err(_) => Err(format!("can't cast {ty:?} to {self:?}")),
}
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct FieldsHint {
pub known_fields: Interned<[FieldType<Interned<dyn TypeHintTrait>>]>,
pub more_fields: bool,
}
impl FieldsHint {
pub fn new(
known_fields: impl IntoIterator<Item = FieldType<Interned<dyn TypeHintTrait>>>,
more_fields: bool,
) -> Self {
let known_fields = Intern::intern_owned(Vec::from_iter(known_fields));
Self {
known_fields,
more_fields,
}
}
pub fn check_field(self, index: usize, field: FieldType<&dyn DynType>) -> Result<(), String> {
let Some(&known_field) = self.known_fields.get(index) else {
return if self.more_fields {
Ok(())
} else {
Err(format!(
"too many fields: name={:?} index={index}",
field.name
))
}; };
}; bundle
let FieldType {
name: known_name,
flipped: known_flipped,
ty: type_hint,
} = known_field;
let FieldType { name, flipped, ty } = field;
if name != known_name {
Err(format!(
"wrong field name {name:?}, expected {known_name:?}"
))
} else if flipped != known_flipped {
Err(format!(
"wrong field direction: flipped={flipped:?}, expected flipped={known_flipped}"
))
} else {
type_hint.matches(ty)
} }
fn source_location() -> SourceLocation {
SourceLocation::builtin()
} }
} }
pub trait BundleType: pub trait BundleType: Type {
Type<CanonicalType = DynBundleType, CanonicalValue = DynBundle> + TypeWithDeref + Connect<Self> fn fields(&self) -> Interned<[BundleField]>;
where }
Self::Value: BundleValue + ToExpr<Type = Self>,
{
type Builder;
fn builder() -> Self::Builder;
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]>;
fn fields_hint() -> FieldsHint;
}
pub trait BundleValue: Value
where
<Self as ToExpr>::Type: BundleType<Value = Self>,
{
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
#[derive(Hash, Eq, PartialEq)]
struct ToBitsMemoize<T>(PhantomData<T>);
impl<T> Clone for ToBitsMemoize<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for ToBitsMemoize<T> {}
impl<T: BundleValue<Type: BundleType<Value = T>>> Memoize for ToBitsMemoize<T> {
type Input = T;
type InputOwned = T;
type Output = Interned<BitSlice>;
fn inner(self, input: &Self::Input) -> Self::Output {
let input = input.to_canonical();
let mut bits = BitVec::with_capacity(input.ty.bit_width());
for field in input.fields.iter() {
bits.extend_from_bitslice(&field.to_bits());
}
Intern::intern_owned(bits)
}
}
ToBitsMemoize::<Self>(PhantomData).get(this)
}
}
pub struct DynBundleMatch;
impl Type for DynBundleType { impl BundleType for Bundle {
type CanonicalType = DynBundleType; fn fields(&self) -> Interned<[BundleField]> {
type Value = DynBundle; self.0.fields
type CanonicalValue = DynBundle; }
type MaskType = DynBundleType; }
type MaskValue = DynBundle;
type MatchVariant = DynBundleMatch; macro_rules! impl_tuples {
([$({#[num = $num:literal] $var:ident: $T:ident})*] []) => {
impl<$($T: Type,)*> Type for ($($T,)*) {
type MaskType = ($($T::MaskType,)*);
type MatchVariant = ($(Expr<$T>,)*);
type MatchActiveScope = (); type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>; type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>; type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants(
fn match_variants<IO: BundleValue>( this: Expr<Self>,
this: Expr<Self::Value>, module_builder: &mut ModuleBuilder<NormalModule>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation, source_location: SourceLocation,
) -> Self::MatchVariantsIter ) -> Self::MatchVariantsIter {
where
IO::Type: BundleType<Value = IO>,
{
let _ = this; let _ = this;
let _ = module_builder; let _ = module_builder;
let _ = source_location; let _ = source_location;
std::iter::once(MatchVariantWithoutScope(DynBundleMatch)) std::iter::once(MatchVariantWithoutScope(($(Expr::field(this, stringify!($num)),)*)))
} }
fn mask_type(&self) -> Self::MaskType { fn mask_type(&self) -> Self::MaskType {
#[derive(Copy, Clone, Eq, PartialEq, Hash)] let ($($var,)*) = self;
struct Impl; ($($var.mask_type(),)*)
impl Memoize for Impl {
type Input = DynBundleType;
type InputOwned = DynBundleType;
type Output = DynBundleType;
fn inner(self, input: &Self::Input) -> Self::Output {
DynBundleType::new(Intern::intern_owned(Vec::from_iter(
input
.fields()
.iter()
.map(|&FieldType { name, flipped, ty }| FieldType {
name,
flipped,
ty: ty.mask_type().canonical(),
}),
)))
} }
fn canonical(&self) -> CanonicalType {
Bundle::new(self.fields()).canonical()
} }
Impl.get(self) #[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
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_eq!(flipped, false);
let $var = $T::from_canonical(ty);)*
($($var,)*)
} }
fn source_location() -> SourceLocation {
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin() SourceLocation::builtin()
} }
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(*self)
} }
impl<$($T: Type,)*> BundleType for ($($T,)*) {
fn from_canonical_type(t: Self::CanonicalType) -> Self { fn fields(&self) -> Interned<[BundleField]> {
t let ($($var,)*) = self;
} [$(BundleField { name: stringify!($num).intern(), flipped: false, ty: $var.canonical() }),*][..].intern()
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
} }
} }
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*) {
pub struct NoBuilder; fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
impl TypeWithDeref for DynBundleType {
fn expr_deref(this: &Expr<Self::Value>) -> &Self::MatchVariant {
let _ = this; let _ = this;
&DynBundleMatch Interned::<_>::into_inner(($(Expr::field(*this, stringify!($num)),)*).intern_sized())
} }
} }
impl<$($T: StaticType,)*> StaticType for ($($T,)*) {
impl Connect<Self> for DynBundleType {} fn static_type() -> Self {
$(let $var = $T::static_type();)*
impl BundleType for DynBundleType { ($($var,)*)
type Builder = NoBuilder;
fn builder() -> Self::Builder {
NoBuilder
}
fn fields(&self) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
self.0.fields
}
fn fields_hint() -> FieldsHint {
FieldsHint {
known_fields: [][..].intern(),
more_fields: true,
} }
} }
}
impl CanonicalType for DynBundleType {
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::BundleType;
}
impl ToExpr for DynBundle {
type Type = DynBundleType;
fn ty(&self) -> Self::Type {
self.ty
}
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
Expr::from_value(self)
}
}
impl Value for DynBundle {
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
self.clone()
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl BundleValue for DynBundle {}
impl CanonicalValue for DynBundle {
fn value_enum_impl(this: &Self) -> ValueEnum {
ValueEnum::Bundle(this.clone())
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
macro_rules! impl_tuple_builder {
($builder:ident, [
$(($before_Ts:ident $before_fields:ident $before_members:literal))*
] [
($T:ident $field:ident $m:literal)
$(($after_Ts:ident $after_fields:ident $after_members:literal))*
]) => {
impl_tuple_builder!($builder, [
$(($before_Ts $before_fields $before_members))*
($T $field $m)
] [
$(($after_Ts $after_fields $after_members))*
]);
impl<Phantom, $($before_Ts,)* $($after_Ts,)*> $builder<
Phantom,
$($before_Ts,)*
(),
$($after_Ts,)*
> {
pub fn $field<$T: ToExpr>(self, $field: $T) -> $builder<
Phantom,
$($before_Ts,)*
Expr<<$T::Type as Type>::Value>,
$($after_Ts,)*
> {
let Self {
$($before_fields,)*
$field: _,
$($after_fields, )*
_phantom: _,
} = self;
let $field = $field.to_expr();
$builder {
$($before_fields,)*
$field,
$($after_fields,)*
_phantom: PhantomData,
}
}
}
};
($builder:ident, [$($before:tt)*] []) => {};
}
macro_rules! into_unit {
($($tt:tt)*) => {
()
};
}
macro_rules! impl_tuple {
($builder:ident, $(($T:ident $T2:ident $field:ident $m:tt)),*) => {
pub struct $builder<Phantom, $($T),*> {
$($field: $T,)*
_phantom: PhantomData<Phantom>,
}
impl_tuple_builder!($builder, [] [$(($T $field $m))*]);
impl<$($T: Value),*> $builder<($($T,)*), $(Expr<$T>,)*>
where
$($T::Type: Type<Value = $T>,)*
{
pub fn build(self) -> Expr<($($T,)*)> {
let Self {
$($field,)*
_phantom: _,
} = self;
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
}
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) { impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
type Type = ($($T::Type,)*); type Type = ($($T::Type,)*);
#[allow(clippy::unused_unit)] fn to_expr(&self) -> Expr<Self::Type> {
fn ty(&self) -> Self::Type { let ($($var,)*) = self;
let ($($field,)*) = self; $(let $var = $var.to_expr();)*
($($field.ty(),)*) let ty = ($(Expr::ty($var),)*);
} let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, &field_values).to_expr()
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
let ($($field,)*) = self;
$(let $field = $field.to_expr();)*
BundleLiteral::new_unchecked(
[$($field.to_canonical_dyn()),*][..].intern(),
($($field.ty(),)*),
).to_expr()
} }
} }
impl<$($T, $T2,)*> Connect<($($T2,)*)> for ($($T,)*)
where
$($T: Connect<$T2>,)*
{
}
impl<$($T: Type,)*> Type for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type CanonicalType = DynBundleType;
type Value = ($($T::Value,)*);
type CanonicalValue = DynBundle;
type MaskType = ($($T::MaskType,)*);
type MaskValue = ($($T::MaskValue,)*);
type MatchVariant = ($(Expr<$T::Value>,)*);
type MatchActiveScope = ();
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Self::MatchVariant>;
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: BundleType<Value = IO>,
{
let _ = this;
let _ = module_builder;
let _ = source_location;
std::iter::once(MatchVariantWithoutScope(($(this.field(stringify!($m)),)*)))
}
#[allow(clippy::unused_unit)]
fn mask_type(&self) -> Self::MaskType {
let ($($field,)*) = self;
($($field.mask_type(),)*)
}
fn canonical(&self) -> Self::CanonicalType {
DynBundleType::new(self.fields())
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::BundleType(self.canonical())
}
#[allow(clippy::unused_unit)]
fn from_canonical_type(t: Self::CanonicalType) -> Self {
let [$($field),*] = *t.fields() else {
panic!("wrong number of fields");
}; };
($($field.from_canonical_type_helper(stringify!($m), false),)*) ([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
} impl_tuples!([$($lhs)*] []);
} impl_tuples!([$($lhs)* $rhs_first] [$($rhs)*]);
impl<$($T: Type,)*> TypeWithDeref for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
fn expr_deref(
this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
let _ = this;
Interned::<_>::into_inner(
Intern::intern_sized((
$(this.field(stringify!($m)),)*
)),
)
}
}
impl<$($T: Type,)*> BundleType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
type Builder = $builder<($($T::Value,)*), $(into_unit!($T),)*>;
fn builder() -> Self::Builder {
$builder {
$($field: (),)*
_phantom: PhantomData,
}
}
fn fields(
&self,
) -> Interned<[FieldType<Interned<dyn DynCanonicalType>>]> {
[
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: self.$m.canonical_dyn(),
},)*
][..].intern()
}
fn fields_hint() -> FieldsHint {
FieldsHint::new([
$(FieldType {
name: stringify!($m).intern(),
flipped: false,
ty: TypeHint::<$T>::intern_dyn(),
},)*
], false)
}
}
impl<$($T: StaticType<MaskType: StaticType>,)*> StaticType for ($($T,)*)
where
$($T::Value: Value<Type = $T>,)*
{
#[allow(clippy::unused_unit)]
fn static_type() -> Self {
($($T::static_type(),)*)
}
}
impl<$($T: Value,)*> Value for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
let ty = self.ty().canonical();
DynBundle::new(
ty,
Arc::new([
$(self.$m.to_canonical_dyn(),)*
]),
)
}
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
BundleValue::to_bits_impl(this)
}
}
impl<$($T: Value,)*> BundleValue for ($($T,)*)
where
$($T::Type: Type<Value = $T>,)*
{
}
}; };
} }
impl_tuple!(TupleBuilder0,); impl_tuples! {
impl_tuple!(TupleBuilder1, (A A2 field_0 0)); [] [
impl_tuple!(TupleBuilder2, (A A2 field_0 0), (B B2 field_1 1)); {#[num = 0] a: A}
impl_tuple!(TupleBuilder3, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2)); {#[num = 1] b: B}
impl_tuple!(TupleBuilder4, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3)); {#[num = 2] c: C}
impl_tuple!(TupleBuilder5, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4)); {#[num = 3] d: D}
impl_tuple!(TupleBuilder6, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5)); {#[num = 4] e: E}
impl_tuple!(TupleBuilder7, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6)); {#[num = 5] f: F}
impl_tuple!(TupleBuilder8, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7)); {#[num = 6] g: G}
impl_tuple!(TupleBuilder9, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8)); {#[num = 7] h: H}
impl_tuple!(TupleBuilder10, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9)); {#[num = 8] i: I}
impl_tuple!(TupleBuilder11, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10)); {#[num = 9] j: J}
impl_tuple!(TupleBuilder12, (A A2 field_0 0), (B B2 field_1 1), (C C2 field_2 2), (D D2 field_3 3), (E E2 field_4 4), (F F2 field_5 5), (G G2 field_6 6), (H H2 field_7 7), (I I2 field_8 8), (J J2 field_9 9), (K K2 field_10 10), (L L2 field_11 11)); {#[num = 10] k: K}
{#[num = 11] l: L}
]
}

View file

@ -1,150 +1,60 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
#![allow(clippy::type_complexity)]
use crate::{ use crate::{
bundle::{BundleValue, TypeHintTrait}, expr::{
expr::{ops::VariantAccess, Expr, ToExpr}, ops::{EnumLiteral, VariantAccess},
int::{UInt, UIntType}, Expr, ToExpr,
intern::{Intern, Interned, MemoizeGeneric}, },
int::Bool,
intern::{Intern, Interned},
module::{ module::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, ModuleBuilder,
NormalModule, Scope, NormalModule, Scope,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{ ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
DynCanonicalValue, DynType, MatchVariantAndInactiveScope, Type, TypeEnum, Value, ValueEnum,
},
}; };
use bitvec::{order::Lsb0, slice::BitSlice, vec::BitVec, view::BitView};
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{ use std::{iter::FusedIterator, ops::Index};
borrow::Cow,
fmt,
hash::{Hash, Hasher},
iter::FusedIterator,
marker::PhantomData,
};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct VariantType<T> { pub struct EnumVariant {
pub name: Interned<str>, pub name: Interned<str>,
pub ty: Option<T>, pub ty: Option<CanonicalType>,
}
pub struct FmtDebugInEnum<'a, T>(&'a VariantType<T>);
impl<T: fmt::Debug> fmt::Debug for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let VariantType { name, ref ty } = *self.0;
if let Some(ty) = ty {
write!(f, "{name}({ty:?})")
} else {
write!(f, "{name}")
}
}
}
impl<T: fmt::Debug> fmt::Display for FmtDebugInEnum<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
impl<T> VariantType<T> {
pub fn map_opt_ty<U, F: FnOnce(Option<T>) -> Option<U>>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType { name, ty: f(ty) }
}
pub fn map_ty<U, F: FnOnce(T) -> U>(self, f: F) -> VariantType<U> {
let Self { name, ty } = self;
VariantType {
name,
ty: ty.map(f),
}
}
pub fn as_ref_ty(&self) -> VariantType<&T> {
VariantType {
name: self.name,
ty: self.ty.as_ref(),
}
}
pub fn fmt_debug_in_enum(&self) -> FmtDebugInEnum<T> {
FmtDebugInEnum(self)
}
}
impl<T: Type> VariantType<T> {
pub fn canonical(&self) -> VariantType<T::CanonicalType> {
self.as_ref_ty().map_ty(T::canonical)
}
pub fn to_dyn(&self) -> VariantType<Interned<dyn DynType>> {
self.as_ref_ty().map_ty(T::to_dyn)
}
pub fn canonical_dyn(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.as_ref_ty().map_ty(T::canonical_dyn)
}
}
impl VariantType<Interned<dyn DynCanonicalType>> {
pub fn from_canonical_type_helper_has_value<T: Type>(self, expected_name: &str) -> T {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
let Some(ty) = self.ty else {
panic!("variant {expected_name} has no value but a value is expected");
};
T::from_dyn_canonical_type(ty)
}
pub fn from_canonical_type_helper_no_value(self, expected_name: &str) {
assert_eq!(&*self.name, expected_name, "variant name doesn't match");
assert!(
self.ty.is_none(),
"variant {expected_name} has a value but is expected to have no value"
);
}
} }
#[derive(Clone, Eq)] #[derive(Clone, Eq)]
struct DynEnumTypeImpl { struct EnumImpl {
variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>, variants: Interned<[EnumVariant]>,
name_indexes: HashMap<Interned<str>, usize>, name_indexes: HashMap<Interned<str>, usize>,
bit_width: usize, type_properties: TypeProperties,
is_storable: bool,
is_castable_from_bits: bool,
} }
impl fmt::Debug for DynEnumTypeImpl { impl std::fmt::Debug for EnumImpl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "DynEnumType ")?; f.debug_struct("Enum")
f.debug_set() .field("variants", &self.variants)
.entries( .field("name_indexes", &self.name_indexes)
self.variants .field("type_properties", &self.type_properties)
.iter()
.map(|variant| variant.fmt_debug_in_enum()),
)
.finish() .finish()
} }
} }
impl PartialEq for DynEnumTypeImpl { impl std::hash::Hash for EnumImpl {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.variants.hash(state);
}
}
impl PartialEq for EnumImpl {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.variants == other.variants self.variants == other.variants
} }
} }
impl Hash for DynEnumTypeImpl { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
fn hash<H: Hasher>(&self, state: &mut H) { pub struct Enum(Interned<EnumImpl>);
self.variants.hash(state);
}
}
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct DynEnumType(Interned<DynEnumTypeImpl>);
impl fmt::Debug for DynEnumType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
fn discriminant_bit_width_impl(variant_count: usize) -> usize { fn discriminant_bit_width_impl(variant_count: usize) -> usize {
variant_count variant_count
@ -153,38 +63,47 @@ fn discriminant_bit_width_impl(variant_count: usize) -> usize {
.unwrap_or(0) as usize .unwrap_or(0) as usize
} }
impl DynEnumType { impl Enum {
#[track_caller] #[track_caller]
pub fn new(variants: Interned<[VariantType<Interned<dyn DynCanonicalType>>]>) -> Self { pub fn new(variants: Interned<[EnumVariant]>) -> Self {
assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208"); assert!(!variants.is_empty(), "zero-variant enums aren't yet supported: https://github.com/chipsalliance/firrtl-spec/issues/208");
let mut name_indexes = HashMap::with_capacity(variants.len()); let mut name_indexes = HashMap::with_capacity(variants.len());
let mut body_bit_width = 0usize; let mut type_properties = TypeProperties {
let mut is_storable = true; is_passive: true,
let mut is_castable_from_bits = true; is_storable: true,
for (index, &VariantType { name, ty }) in variants.iter().enumerate() { is_castable_from_bits: true,
if let Some(old_index) = name_indexes.insert(name, index) { bit_width: 0,
};
for (index, EnumVariant { 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}"); panic!("duplicate variant name {name:?}: at both index {old_index} and {index}");
} }
if let Some(ty) = ty { if let Some(ty) = ty {
assert!( let TypeProperties {
ty.is_passive(), is_passive,
"variant type must be a passive type: {ty:?}" is_storable,
); is_castable_from_bits,
body_bit_width = body_bit_width.max(ty.bit_width()); bit_width,
is_storable &= ty.is_storable(); } = ty.type_properties();
is_castable_from_bits &= ty.is_castable_from_bits(); assert!(is_passive, "variant type must be a passive type: {ty:?}");
type_properties = TypeProperties {
is_passive: true,
is_storable: type_properties.is_storable & is_storable,
is_castable_from_bits: type_properties.is_castable_from_bits
& is_castable_from_bits,
bit_width: type_properties.bit_width.max(bit_width),
};
} }
} }
let bit_width = body_bit_width type_properties.bit_width = type_properties
.bit_width
.checked_add(discriminant_bit_width_impl(variants.len())) .checked_add(discriminant_bit_width_impl(variants.len()))
.unwrap_or_else(|| panic!("enum is too big: bit-width overflowed")); .unwrap_or_else(|| panic!("enum is too big: bit-width overflowed"));
Self( Self(
DynEnumTypeImpl { EnumImpl {
variants, variants,
name_indexes, name_indexes,
bit_width, type_properties,
is_storable,
is_castable_from_bits,
} }
.intern_sized(), .intern_sized(),
) )
@ -192,233 +111,31 @@ impl DynEnumType {
pub fn discriminant_bit_width(self) -> usize { pub fn discriminant_bit_width(self) -> usize {
discriminant_bit_width_impl(self.variants().len()) discriminant_bit_width_impl(self.variants().len())
} }
pub fn is_passive(self) -> bool { pub fn type_properties(self) -> TypeProperties {
true self.0.type_properties
}
pub fn is_storable(self) -> bool {
self.0.is_storable
}
pub fn is_castable_from_bits(self) -> bool {
self.0.is_castable_from_bits
}
pub fn bit_width(self) -> usize {
self.0.bit_width
} }
pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> { pub fn name_indexes(&self) -> &HashMap<Interned<str>, usize> {
&self.0.name_indexes &self.0.name_indexes
} }
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct DynEnum {
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
}
impl DynEnum {
#[track_caller]
pub fn new_by_index(
ty: DynEnumType,
variant_index: usize,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant = ty.variants()[variant_index];
assert_eq!(
variant_value.as_ref().map(|v| v.ty()),
variant.ty,
"variant value doesn't match type"
);
Self {
ty,
variant_index,
variant_value,
}
}
#[track_caller]
pub fn new_by_name(
ty: DynEnumType,
variant_name: Interned<str>,
variant_value: Option<DynCanonicalValue>,
) -> Self {
let variant_index = ty.name_indexes()[&variant_name];
Self::new_by_index(ty, variant_index, variant_value)
}
pub fn variant_index(&self) -> usize {
self.variant_index
}
pub fn variant_value(&self) -> &Option<DynCanonicalValue> {
&self.variant_value
}
pub fn variant_with_type(&self) -> VariantType<Interned<dyn DynCanonicalType>> {
self.ty.variants()[self.variant_index]
}
pub fn variant_name(&self) -> Interned<str> {
self.variant_with_type().name
}
pub fn variant_type(&self) -> Option<Interned<dyn DynCanonicalType>> {
self.variant_with_type().ty
}
pub fn variant_with_value(&self) -> VariantType<&DynCanonicalValue> {
self.variant_with_type()
.map_opt_ty(|_| self.variant_value.as_ref())
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct VariantsHint {
pub known_variants: Interned<[VariantType<Interned<dyn TypeHintTrait>>]>,
pub more_variants: bool,
}
impl VariantsHint {
pub fn new(
known_variants: impl IntoIterator<Item = VariantType<Interned<dyn TypeHintTrait>>>,
more_variants: bool,
) -> Self {
let known_variants = Intern::intern_owned(Vec::from_iter(known_variants));
Self {
known_variants,
more_variants,
}
}
pub fn check_variant(
self,
index: usize,
variant: VariantType<&dyn DynType>,
) -> Result<(), String> {
let Some(&known_variant) = self.known_variants.get(index) else {
return if self.more_variants {
Ok(())
} else {
Err(format!(
"too many variants: name={:?} index={index}",
variant.name
))
};
};
let VariantType {
name: known_name,
ty: type_hint,
} = known_variant;
let VariantType { name, ty } = variant;
if name != known_name {
Err(format!(
"wrong variant name {name:?}, expected {known_name:?}"
))
} else {
match (ty, type_hint) {
(Some(ty), Some(type_hint)) => type_hint.matches(ty),
(None, None) => Ok(()),
(None, Some(_)) => Err(format!(
"expected variant {name:?} to have type, no type provided"
)),
(Some(_), None) => Err(format!(
"expected variant {name:?} to have no type, but a type was provided"
)),
}
}
}
}
pub trait EnumType: pub trait EnumType:
Type< Type<
CanonicalType = DynEnumType, MaskType = Bool,
CanonicalValue = DynEnum,
MaskType = UIntType<1>,
MaskValue = UInt<1>,
MatchActiveScope = Scope, MatchActiveScope = Scope,
MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>, MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>,
MatchVariantsIter = EnumMatchVariantsIter<Self>, MatchVariantsIter = EnumMatchVariantsIter<Self>,
> + Connect<Self> >
where
Self::Value: EnumValue + ToExpr<Type = Self>,
{ {
type Builder; fn variants(&self) -> Interned<[EnumVariant]>;
fn match_activate_scope( fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope, v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope); ) -> (Self::MatchVariant, Self::MatchActiveScope);
fn builder() -> Self::Builder;
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]>;
fn variants_hint() -> VariantsHint;
#[allow(clippy::result_unit_err)]
fn variant_to_bits<VariantValue: ToExpr + Eq + Hash + Send + Sync + 'static + Clone>(
&self,
variant_index: usize,
variant_value: Option<&VariantValue>,
) -> Result<Interned<BitSlice>, ()> {
#[derive(Hash, Eq, PartialEq)]
struct VariantToBitsMemoize<E, V>(PhantomData<(E, V)>);
impl<E, V> Clone for VariantToBitsMemoize<E, V> {
fn clone(&self) -> Self {
*self
}
}
impl<E, V> Copy for VariantToBitsMemoize<E, V> {}
impl<
E: EnumType<Value: EnumValue<Type = E>>,
V: ToExpr + Eq + Hash + Send + Sync + 'static + Clone,
> MemoizeGeneric for VariantToBitsMemoize<E, V>
{
type InputRef<'a> = (&'a E, usize, Option<&'a V>);
type InputOwned = (E, usize, Option<V>);
type InputCow<'a> = (Cow<'a, E>, usize, Option<Cow<'a, V>>);
type Output = Result<Interned<BitSlice>, ()>;
fn input_borrow(input: &Self::InputOwned) -> Self::InputRef<'_> {
(&input.0, input.1, input.2.as_ref())
}
fn input_eq(a: Self::InputRef<'_>, b: Self::InputRef<'_>) -> bool {
a == b
}
fn input_cow_into_owned(input: Self::InputCow<'_>) -> Self::InputOwned {
(input.0.into_owned(), input.1, input.2.map(Cow::into_owned))
}
fn input_cow_borrow<'a>(input: &'a Self::InputCow<'_>) -> Self::InputRef<'a> {
(&input.0, input.1, input.2.as_deref())
}
fn input_cow_from_owned<'a>(input: Self::InputOwned) -> Self::InputCow<'a> {
(Cow::Owned(input.0), input.1, input.2.map(Cow::Owned))
}
fn input_cow_from_ref(input: Self::InputRef<'_>) -> Self::InputCow<'_> {
(Cow::Borrowed(input.0), input.1, input.2.map(Cow::Borrowed))
}
fn inner(self, input: Self::InputRef<'_>) -> Self::Output {
let (ty, variant_index, variant_value) = input;
let ty = ty.canonical();
let mut bits = BitVec::with_capacity(ty.bit_width());
bits.extend_from_bitslice(
&variant_index.view_bits::<Lsb0>()[..ty.discriminant_bit_width()],
);
if let Some(variant_value) = variant_value {
bits.extend_from_bitslice(&variant_value.to_expr().to_literal_bits()?);
}
bits.resize(ty.bit_width(), false);
Ok(Intern::intern_owned(bits))
}
}
VariantToBitsMemoize::<Self, VariantValue>(PhantomData).get((
self,
variant_index,
variant_value,
))
}
} }
pub trait EnumValue: Value pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>);
where
<Self as ToExpr>::Type: EnumType<Value = Self>,
{
}
pub struct EnumMatchVariantAndInactiveScope<T: EnumType>(EnumMatchVariantAndInactiveScopeImpl<T>) impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T> {
where
T::Value: EnumValue<Type = T>;
impl<T: EnumType> MatchVariantAndInactiveScope for EnumMatchVariantAndInactiveScope<T>
where
T::Value: EnumValue<Type = T>,
{
type MatchVariant = T::MatchVariant; type MatchVariant = T::MatchVariant;
type MatchActiveScope = Scope; type MatchActiveScope = Scope;
@ -427,36 +144,22 @@ where
} }
} }
impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> impl<T: EnumType> EnumMatchVariantAndInactiveScope<T> {
where pub fn variant_access(&self) -> Interned<VariantAccess> {
T::Value: EnumValue<Type = T>,
{
pub fn variant_access(&self) -> Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>> {
self.0.variant_access() self.0.variant_access()
} }
pub fn activate( pub fn activate(self) -> (Interned<VariantAccess>, Scope) {
self,
) -> (
Interned<VariantAccess<T, Interned<dyn DynCanonicalType>>>,
Scope,
) {
self.0.activate() self.0.activate()
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct EnumMatchVariantsIter<T: EnumType> pub struct EnumMatchVariantsIter<T: EnumType> {
where
T::Value: EnumValue<Type = T>,
{
pub(crate) inner: EnumMatchVariantsIterImpl<T>, pub(crate) inner: EnumMatchVariantsIterImpl<T>,
pub(crate) variant_index: std::ops::Range<usize>, pub(crate) variant_index: std::ops::Range<usize>,
} }
impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> impl<T: EnumType> Iterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
type Item = EnumMatchVariantAndInactiveScope<T>; type Item = EnumMatchVariantAndInactiveScope<T>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
@ -470,21 +173,15 @@ where
} }
} }
impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> impl<T: EnumType> ExactSizeIterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
fn len(&self) -> usize { fn len(&self) -> usize {
self.variant_index.len() self.variant_index.len()
} }
} }
impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> where T::Value: EnumValue<Type = T> {} impl<T: EnumType> FusedIterator for EnumMatchVariantsIter<T> {}
impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> impl<T: EnumType> DoubleEndedIterator for EnumMatchVariantsIter<T> {
where
T::Value: EnumValue<Type = T>,
{
fn next_back(&mut self) -> Option<Self::Item> { fn next_back(&mut self) -> Option<Self::Item> {
self.variant_index.next_back().map(|variant_index| { self.variant_index.next_back().map(|variant_index| {
EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index)) EnumMatchVariantAndInactiveScope(self.inner.for_variant_index(variant_index))
@ -492,129 +189,173 @@ where
} }
} }
impl Type for DynEnumType { impl EnumType for Enum {
type CanonicalType = DynEnumType;
type Value = DynEnum;
type CanonicalValue = DynEnum;
type MaskType = UIntType<1>;
type MaskValue = UInt<1>;
type MatchVariant = Option<Expr<DynCanonicalValue>>;
type MatchActiveScope = Scope;
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
fn match_variants<IO: BundleValue>(
this: Expr<Self::Value>,
module_builder: &mut ModuleBuilder<IO, NormalModule>,
source_location: SourceLocation,
) -> Self::MatchVariantsIter
where
IO::Type: crate::bundle::BundleType<Value = IO>,
{
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> Self::MaskType {
UIntType::new()
}
fn canonical(&self) -> Self::CanonicalType {
*self
}
fn source_location(&self) -> SourceLocation {
SourceLocation::builtin()
}
fn type_enum(&self) -> TypeEnum {
TypeEnum::EnumType(*self)
}
fn from_canonical_type(t: Self::CanonicalType) -> Self {
t
}
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
Some(this)
}
}
impl Connect<Self> for DynEnumType {}
pub struct NoBuilder;
impl EnumType for DynEnumType {
type Builder = NoBuilder;
fn match_activate_scope( fn match_activate_scope(
v: Self::MatchVariantAndInactiveScope, v: Self::MatchVariantAndInactiveScope,
) -> (Self::MatchVariant, Self::MatchActiveScope) { ) -> (Self::MatchVariant, Self::MatchActiveScope) {
let (expr, scope) = v.0.activate(); let (expr, scope) = v.0.activate();
(expr.variant_type().ty.map(|_| expr.to_expr()), scope) (expr.variant_type().map(|_| expr.to_expr()), scope)
} }
fn variants(&self) -> Interned<[EnumVariant]> {
fn builder() -> Self::Builder {
NoBuilder
}
fn variants(&self) -> Interned<[VariantType<Interned<dyn DynCanonicalType>>]> {
self.0.variants self.0.variants
} }
fn variants_hint() -> VariantsHint {
VariantsHint {
known_variants: [][..].intern(),
more_variants: true,
} }
impl Type for Enum {
type MaskType = Bool;
type MatchVariant = Option<Expr<CanonicalType>>;
type MatchActiveScope = Scope;
type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
type MatchVariantsIter = EnumMatchVariantsIter<Self>;
fn match_variants(
this: Expr<Self>,
module_builder: &mut ModuleBuilder<NormalModule>,
source_location: crate::source_location::SourceLocation,
) -> Self::MatchVariantsIter {
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> Self::MaskType {
Bool
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Enum(*self)
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Enum(retval) = canonical_type else {
panic!("expected enum");
};
retval
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
} }
} }
impl CanonicalType for DynEnumType { #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::EnumType; pub struct HdlOption<T: Type> {
some_ty: T,
} }
impl ToExpr for DynEnum { impl<T: Type> HdlOption<T> {
type Type = DynEnumType; pub const fn new(some_ty: T) -> Self {
Self { some_ty }
fn ty(&self) -> Self::Type {
self.ty
} }
pub const fn some_ty(&self) -> &T {
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> { &self.some_ty
Expr::from_value(self) }
#[allow(non_snake_case)]
pub fn None(self) -> Expr<Self> {
EnumLiteral::new(self, 0, None).to_expr()
}
#[allow(non_snake_case)]
pub fn Some<V: ToExpr<Type = T>>(self, v: V) -> Expr<Self> {
EnumLiteral::new(self, 1, Some(Expr::canonical(v.to_expr()))).to_expr()
} }
} }
impl Value for DynEnum { #[allow(non_upper_case_globals)]
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue { pub const HdlOption: HdlOptionWithoutGenerics = HdlOptionWithoutGenerics;
self.clone()
} #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { pub struct HdlOptionWithoutGenerics;
this.ty
.variant_to_bits(this.variant_index, this.variant_value.as_ref()) impl<T: Type> Index<T> for HdlOptionWithoutGenerics {
.unwrap() type Output = HdlOption<T>;
fn index(&self, some_ty: T) -> &Self::Output {
Interned::<_>::into_inner(Intern::intern_sized(HdlOption::new(some_ty)))
} }
} }
impl EnumValue for DynEnum {} impl<T: Type> EnumType for HdlOption<T> {
fn match_activate_scope(
impl CanonicalValue for DynEnum { v: Self::MatchVariantAndInactiveScope,
fn value_enum_impl(this: &Self) -> ValueEnum { ) -> (Self::MatchVariant, Self::MatchActiveScope) {
ValueEnum::Enum(this.clone()) let (variant_access, scope) = v.activate();
(
match variant_access.variant_index() {
0 => None,
1 => Some(
VariantAccess::new_unchecked(
variant_access.base(),
variant_access.variant_index(),
)
.to_expr(),
),
2.. => panic!("invalid variant index"),
},
scope,
)
} }
fn to_bits_impl(this: &Self) -> Interned<BitSlice> { fn variants(&self) -> Interned<[EnumVariant]> {
this.ty [
.variant_to_bits(this.variant_index, this.variant_value.as_ref()) EnumVariant {
.unwrap() name: "None".intern(),
ty: None,
},
EnumVariant {
name: "Some".intern(),
ty: Some(self.some_ty().canonical()),
},
][..]
.intern()
} }
} }
mod impl_option { impl<T: Type> Type for HdlOption<T> {
#[allow(dead_code)] type MaskType = Bool;
#[derive(crate::ty::Value)] type MatchVariant = Option<Expr<T>>;
#[hdl(target(std::option::Option), connect_inexact, outline_generated)] type MatchActiveScope = Scope;
pub enum Option<T> { type MatchVariantAndInactiveScope = EnumMatchVariantAndInactiveScope<Self>;
None, type MatchVariantsIter = EnumMatchVariantsIter<Self>;
Some(T),
fn match_variants(
this: Expr<Self>,
module_builder: &mut ModuleBuilder<NormalModule>,
source_location: crate::source_location::SourceLocation,
) -> Self::MatchVariantsIter {
module_builder.enum_match_variants_helper(this, source_location)
}
fn mask_type(&self) -> Self::MaskType {
Bool
}
fn canonical(&self) -> CanonicalType {
CanonicalType::Enum(Enum::new(self.variants()))
}
#[track_caller]
fn from_canonical(canonical_type: CanonicalType) -> Self {
let CanonicalType::Enum(enum_) = canonical_type else {
panic!("expected enum");
};
let [none, some] = *enum_.variants() else {
panic!("enum has wrong number of variants");
};
let EnumVariant {
name: none_name,
ty: none_ty,
} = none;
assert_eq!(&*none_name, "None");
assert!(none_ty.is_none());
let EnumVariant {
name: some_name,
ty: some_ty,
} = some;
assert_eq!(&*some_name, "Some");
let some_ty = T::from_canonical(some_ty.expect("Some has a value"));
Self { some_ty }
}
fn source_location() -> SourceLocation {
SourceLocation::builtin()
}
}
impl<T: StaticType> StaticType for HdlOption<T> {
fn static_type() -> Self {
HdlOption::new(T::static_type())
} }
} }

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -12,7 +12,6 @@ extern crate self as fayalite;
pub use std as __std; pub use std as __std;
#[doc(inline)] #[doc(inline)]
#[doc(alias = "hdl")]
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
/// a [`Module`][`::fayalite::module::Module`] when called. /// a [`Module`][`::fayalite::module::Module`] when called.
/// In the function body it will implicitly create a /// In the function body it will implicitly create a
@ -21,25 +20,82 @@ pub use std as __std;
/// See [Fayalite Modules][crate::_docs::modules] /// See [Fayalite Modules][crate::_docs::modules]
pub use fayalite_proc_macros::hdl_module; pub use fayalite_proc_macros::hdl_module;
#[doc(inline)]
pub use fayalite_proc_macros::hdl;
#[cfg(feature = "unstable-doc")] #[cfg(feature = "unstable-doc")]
pub mod _docs; pub mod _docs;
pub mod annotations; // FIXME: finish
//pub mod annotations;
pub mod array; pub mod array;
pub mod bundle; pub mod bundle;
pub mod cli; //pub mod cli;
pub mod clock; //pub mod clock;
pub mod enum_; pub mod enum_;
pub mod expr; pub mod expr;
pub mod firrtl; //pub mod firrtl;
pub mod int; pub mod int;
pub mod intern; pub mod intern;
pub mod memory; //pub mod memory;
pub mod module; //pub mod module;
pub mod reg; //pub mod reg;
pub mod reset; //pub mod reset;
pub mod source_location; pub mod source_location;
pub mod ty; pub mod ty;
pub mod util; pub mod util;
pub mod valueless; //pub mod valueless;
pub mod wire; //pub mod wire;
// FIXME: switch to real module mod
pub mod module {
use crate::{
enum_::{EnumMatchVariantsIter, EnumType},
expr::{ops::VariantAccess, Expr},
intern::Interned,
source_location::SourceLocation,
};
use std::marker::PhantomData;
pub struct NormalModule;
pub struct ModuleBuilder<ModuleKind>(PhantomData<ModuleKind>);
impl ModuleBuilder<NormalModule> {
pub fn enum_match_variants_helper<Enum: EnumType>(
&mut self,
_base: Expr<Enum>,
_source_location: SourceLocation,
) -> EnumMatchVariantsIter<Enum> {
todo!()
}
}
pub struct Scope(());
pub(crate) struct EnumMatchVariantAndInactiveScopeImpl<T: EnumType> {
variant_access: Interned<VariantAccess>,
_phantom: PhantomData<T>,
}
impl<T: EnumType> EnumMatchVariantAndInactiveScopeImpl<T> {
pub(crate) fn activate(self) -> (Interned<VariantAccess>, Scope) {
(self.variant_access, Scope(()))
}
pub(crate) fn variant_access(&self) -> Interned<VariantAccess> {
self.variant_access
}
}
#[derive(Clone)]
pub(crate) struct EnumMatchVariantsIterImpl<T>(PhantomData<T>);
impl<T: EnumType> EnumMatchVariantsIterImpl<T> {
pub(crate) fn for_variant_index(
&self,
_variant_index: usize,
) -> EnumMatchVariantAndInactiveScopeImpl<T> {
todo!()
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -3,10 +3,13 @@
mod const_bool; mod const_bool;
mod const_cmp; mod const_cmp;
mod const_usize;
mod misc; mod misc;
#[doc(inline)] #[doc(inline)]
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool}; pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
#[doc(inline)]
pub use const_usize::{ConstUsize, GenericConstUsize};
#[doc(inline)] #[doc(inline)]
pub use const_cmp::{ pub use const_cmp::{

View file

@ -0,0 +1,29 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use std::{fmt::Debug, hash::Hash};
mod sealed {
pub trait Sealed {}
}
/// the only implementation is `ConstUsize<Self::VALUE>`
pub trait GenericConstUsize:
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
{
const VALUE: usize;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct ConstUsize<const VALUE: usize>;
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
}
}
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
const VALUE: usize = VALUE;
}

View file

@ -1,17 +1,15 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use fayalite::{ use fayalite::{hdl, int::UInt, ty::Type};
int::UInt,
ty::{StaticValue, Value},
};
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub struct S<T> { pub struct S<T: Type> {
pub a: T, pub a: T,
b: UInt<3>, b: UInt<3>,
} }
/// FIXME: reactivate
#[cfg(false_)]
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated)] #[hdl(outline_generated)]
pub enum E<T> { pub enum E<T> {
@ -25,6 +23,8 @@ pub enum E<T> {
H(T, UInt<1>), H(T, UInt<1>),
} }
/// FIXME: reactivate
#[cfg(false_)]
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)] #[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
#[hdl(outline_generated, static, where(T: StaticValue))] #[hdl(outline_generated, static, where(T: StaticValue))]
pub struct S2<T> { pub struct S2<T> {