1
0
Fork 0

Compare commits

...

10 commits

48 changed files with 6708 additions and 1566 deletions

View file

@ -396,15 +396,29 @@ impl ToTokens for Builder {
quote_spanned! {self.ident.span()=>
#[automatically_derived]
#[allow(non_camel_case_types, dead_code, private_interfaces)]
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
impl #filled_impl_generics ::fayalite::expr::ValueType for #filled_ty
#filled_where_clause
{
type Type = #target #type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategoryExpr;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
#target {
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)*
}
}
}
#[automatically_derived]
#[allow(non_camel_case_types, dead_code, private_interfaces)]
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
#filled_where_clause
{
fn to_expr(
&self,
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ValueType>::Type> {
let __ty = #target {
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
#(#field_idents: ::fayalite::expr::ValueType::ty(&self.#field_idents),)*
};
let __field_values = [
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
@ -695,10 +709,10 @@ impl ToTokens for ParsedBundle {
v.field(&value.#ident);
}
}));
let to_sim_value_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let value_type_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
let ident: &Ident = field.ident().as_ref().unwrap();
quote_spanned! {span=>
#ident: ::fayalite::sim::value::SimValue::ty(&self.#ident),
#ident: ::fayalite::expr::ValueType::ty(&self.#ident),
}
}));
let fields_len = fields.named().into_iter().len();
@ -806,28 +820,39 @@ impl ToTokens for ParsedBundle {
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics
#where_clause
{
type Type = #mask_type_ident #type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
#mask_type_ident {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #mask_type_sim_value_ident #type_generics
#where_clause
{
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
let ty = #mask_type_ident {
#(#to_sim_value_fields)*
#(#value_type_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
let ty = #mask_type_ident {
#(#to_sim_value_fields)*
#(#value_type_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, self)
}
@ -955,28 +980,39 @@ impl ToTokens for ParsedBundle {
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics
#where_clause
{
type Type = #target #type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
#target {
#(#value_type_fields)*
}
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::ToSimValue for #sim_value_ident #type_generics
#where_clause
{
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
let ty = #target {
#(#to_sim_value_fields)*
#(#value_type_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, ::fayalite::__std::clone::Clone::clone(self))
}
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
let ty = #target {
#(#to_sim_value_fields)*
#(#value_type_fields)*
};
::fayalite::sim::value::SimValue::from_value(ty, self)
}
@ -1002,91 +1038,172 @@ impl ToTokens for ParsedBundle {
}
.to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq {
let mut expr_where_clause =
let mut cmp_eq_where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut sim_value_where_clause = expr_where_clause.clone();
let mut fields_sim_value_eq = vec![];
let mut fields_cmp_eq = vec![];
let mut fields_cmp_ne = vec![];
let mut fields_value_eq = vec![];
let mut fields_value_ne = vec![];
let mut fields_expr_eq = vec![];
let mut fields_expr_ne = vec![];
let mut fields_valueless_eq = vec![];
let mut fields_valueless_ne = vec![];
for field in fields.named() {
let field_ident = field.ident();
let field_ty = field.ty();
expr_where_clause
cmp_eq_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
#field_ty: ::fayalite::expr::HdlPartialEqImpl<#field_ty>
});
sim_value_where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::sim::value::SimValuePartialEq<#field_ty>
});
fields_sim_value_eq.push(quote_spanned! {span=>
::fayalite::sim::value::SimValuePartialEq::sim_value_eq(&__lhs.#field_ident, &__rhs.#field_ident)
fields_value_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_eq(
__lhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
__rhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
)
});
fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
fields_value_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_value_ne(
__lhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__lhs_value.#field_ident),
__rhs.#field_ident,
::fayalite::__std::borrow::Cow::Borrowed(&__rhs_value.#field_ident),
)
});
fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
fields_expr_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_expr_eq(
__lhs.#field_ident,
__rhs.#field_ident,
)
});
fields_expr_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_expr_ne(
__lhs.#field_ident,
__rhs.#field_ident,
)
});
fields_valueless_eq.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_eq(
::fayalite::expr::Valueless::new(__lhs.#field_ident),
::fayalite::expr::Valueless::new(__rhs.#field_ident),
)
});
fields_valueless_ne.push(quote_spanned! {span=>
::fayalite::expr::HdlPartialEqImpl::cmp_valueless_ne(
::fayalite::expr::Valueless::new(__lhs.#field_ident),
::fayalite::expr::Valueless::new(__rhs.#field_ident),
)
});
}
let sim_value_eq_body;
let cmp_eq_body;
let cmp_ne_body;
let value_eq_body;
let value_ne_body;
let expr_eq_body;
let expr_ne_body;
let valueless_eq_body;
let valueless_ne_body;
if fields_len == 0 {
sim_value_eq_body = quote_spanned! {span=>
value_eq_body = quote_spanned! {span=>
true
};
cmp_eq_body = quote_spanned! {span=>
value_ne_body = quote_spanned! {span=>
false
};
expr_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true)
};
cmp_ne_body = quote_spanned! {span=>
expr_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false)
};
valueless_eq_body = quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
};
valueless_ne_body = quote_spanned! {span=>
::fayalite::expr::Valueless::new(::fayalite::int::Bool)
};
} else {
sim_value_eq_body = quote_spanned! {span=>
#(#fields_sim_value_eq)&&*
value_eq_body = quote_spanned! {span=>
#(#fields_value_eq)&*
};
cmp_eq_body = quote_spanned! {span=>
#(#fields_cmp_eq)&*
value_ne_body = quote_spanned! {span=>
#(#fields_value_ne)|*
};
cmp_ne_body = quote_spanned! {span=>
#(#fields_cmp_ne)|*
expr_eq_body = quote_spanned! {span=>
#(#fields_expr_eq)&*
};
expr_ne_body = quote_spanned! {span=>
#(#fields_expr_ne)|*
};
valueless_eq_body = quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_eq)|*
};
valueless_ne_body = quote_spanned! {span=>
let __lhs = ::fayalite::expr::ValueType::ty(&__lhs);
let __rhs = ::fayalite::expr::ValueType::ty(&__rhs);
#(#fields_valueless_ne)|*
};
};
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
#expr_where_clause
impl #impl_generics ::fayalite::expr::HdlPartialEqImpl<Self> for #target #type_generics
#cmp_eq_where_clause
{
fn cmp_eq(
#[track_caller]
fn cmp_value_eq(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
#value_eq_body
}
#[track_caller]
fn cmp_value_ne(
__lhs: Self,
__lhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
__rhs: Self,
__rhs_value: ::fayalite::__std::borrow::Cow<'_, <Self as ::fayalite::ty::Type>::SimValue>,
) -> ::fayalite::__std::primitive::bool {
#value_ne_body
}
#[track_caller]
fn cmp_expr_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_eq_body
#expr_eq_body
}
fn cmp_ne(
#[track_caller]
fn cmp_expr_ne(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_ne_body
#expr_ne_body
}
}
#[automatically_derived]
impl #impl_generics ::fayalite::sim::value::SimValuePartialEq<Self> for #target #type_generics
#sim_value_where_clause
{
fn sim_value_eq(
__lhs: &::fayalite::sim::value::SimValue<Self>,
__rhs: &::fayalite::sim::value::SimValue<Self>,
) -> bool {
#sim_value_eq_body
#[track_caller]
fn cmp_valueless_eq(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_eq_body
}
#[track_caller]
fn cmp_valueless_ne(
__lhs: ::fayalite::expr::Valueless<Self>,
__rhs: ::fayalite::expr::Valueless<Self>,
) -> ::fayalite::expr::Valueless<::fayalite::int::Bool> {
#valueless_ne_body
}
}
}

View file

@ -1024,16 +1024,26 @@ impl ToTokens for ParsedEnum {
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
impl #static_impl_generics ::fayalite::expr::ValueType
for #sim_value_ident #static_type_generics
#static_where_clause
{
type Type = #target #static_type_generics;
type ValueCategory = ::fayalite::expr::value_category::ValueCategorySimValue;
fn ty(&self) -> <Self as ::fayalite::expr::ValueType>::Type {
::fayalite::ty::StaticType::TYPE
}
}
#[automatically_derived]
impl #static_impl_generics ::fayalite::sim::value::ToSimValue
for #sim_value_ident #static_type_generics
#static_where_clause
{
fn to_sim_value(
&self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE,
@ -1043,7 +1053,7 @@ impl ToTokens for ParsedEnum {
fn into_sim_value(
self,
) -> ::fayalite::sim::value::SimValue<
<Self as ::fayalite::sim::value::ToSimValue>::Type,
<Self as ::fayalite::expr::ValueType>::Type,
> {
::fayalite::sim::value::SimValue::from_value(
::fayalite::ty::StaticType::TYPE,

View file

@ -887,7 +887,13 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
}
}
let _print_on_panic = PrintOnPanic(&contents);
let contents = prettyplease::unparse(&parse_quote! { #contents });
let mut parse_err = None;
let (Ok(contents) | Err(contents)) = syn::parse2(contents.clone())
.map(|file| prettyplease::unparse(&file))
.map_err(|e| {
parse_err = Some(e);
contents.to_string()
});
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
let hash = base16ct::HexDisplay(&hash[..5]);
file.write_all(contents.as_bytes()).unwrap();
@ -899,9 +905,26 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
e.unwrap();
}
}
eprintln!("generated {}", dest_file.display());
let log_msg = if let Some(parse_err) = parse_err {
format!(
"fayalite-proc-macros-impl internal error:\nfailed to parse generated output: {parse_err}\nunformatted output is in: {}\n",
dest_file.display()
)
} else {
format!("generated {}\n", dest_file.display())
};
// write message atomically if possible
let mut stderr = std::io::stderr().lock();
let write_result = stderr.write_all(log_msg.as_bytes());
let flush_result = stderr.flush();
drop(stderr); // unlock before we try to panic
write_result.unwrap();
flush_result.unwrap();
std::io::stderr()
.lock()
.write_all(log_msg.as_bytes())
.unwrap();
let dest_file = dest_file.to_str().unwrap();
quote! {
include!(#dest_file);
}

View file

@ -88,6 +88,9 @@ impl Visitor<'_> {
field.expr = parse_quote_spanned! {field.member.span()=>
::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr))
};
field
.colon_token
.get_or_insert(Token![:](field.member.span()));
}
return parse_quote_spanned! {name_span=>
{

View file

@ -10,7 +10,7 @@ use crate::{
};
use proc_macro2::{Span, TokenStream};
use quote::{ToTokens, TokenStreamExt, format_ident, quote_spanned};
use std::collections::BTreeSet;
use std::collections::BTreeMap;
use syn::{
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Local, Member, Pat, PatIdent, PatOr,
PatParen, PatPath, PatRest, PatStruct, PatTuple, PatTupleStruct, PatWild, Path, PathSegment,
@ -24,65 +24,65 @@ use syn::{
macro_rules! visit_trait {
(
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &$Value:ty) $block:block)*
$($vis:vis fn $fn:ident($state:ident: _, $value:ident: &mut $Value:ty) $block:block)*
) => {
trait VisitMatchPat<'a> {
$(fn $fn(&mut self, $value: &'a $Value) {
$(fn $fn(&mut self, $value: &'a mut $Value) {
$fn(self, $value);
})*
}
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a $Value) $block)*
$($vis fn $fn<'a>($state: &mut (impl ?Sized + VisitMatchPat<'a>), $value: &'a mut $Value) $block)*
};
}
visit_trait! {
fn visit_match_pat_binding(_state: _, v: &MatchPatBinding) {
let MatchPatBinding { ident: _ } = v;
fn visit_match_pat_binding(_state: _, v: &mut MatchPatBinding) {
let MatchPatBinding { mutability: _, ident: _ } = v;
}
fn visit_match_pat_wild(_state: _, v: &MatchPatWild) {
fn visit_match_pat_wild(_state: _, v: &mut MatchPatWild) {
let MatchPatWild { underscore_token: _ } = v;
}
fn visit_match_pat_rest(_state: _, v: &MatchPatRest) {
fn visit_match_pat_rest(_state: _, v: &mut MatchPatRest) {
let MatchPatRest { dot2_token: _ } = v;
}
fn visit_match_pat_paren(state: _, v: &MatchPatParen<MatchPat>) {
fn visit_match_pat_paren(state: _, v: &mut MatchPatParen<MatchPat>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat(pat);
}
fn visit_match_pat_paren_simple(state: _, v: &MatchPatParen<MatchPatSimple>) {
fn visit_match_pat_paren_simple(state: _, v: &mut MatchPatParen<MatchPatSimple>) {
let MatchPatParen { paren_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_or(state: _, v: &MatchPatOr<MatchPat>) {
fn visit_match_pat_or(state: _, v: &mut MatchPatOr<MatchPat>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat(v);
}
}
fn visit_match_pat_or_simple(state: _, v: &MatchPatOr<MatchPatSimple>) {
fn visit_match_pat_or_simple(state: _, v: &mut MatchPatOr<MatchPatSimple>) {
let MatchPatOr { leading_vert: _, cases } = v;
for v in cases {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_struct_field(state: _, v: &MatchPatStructField) {
fn visit_match_pat_struct_field(state: _, v: &mut MatchPatStructField) {
let MatchPatStructField { field_name: _, colon_token: _, pat } = v;
state.visit_match_pat_simple(pat);
}
fn visit_match_pat_struct(state: _, v: &MatchPatStruct) {
fn visit_match_pat_struct(state: _, v: &mut MatchPatStruct) {
let MatchPatStruct { match_span: _, path: _, brace_token: _, fields, rest: _ } = v;
for v in fields {
state.visit_match_pat_struct_field(v);
}
}
fn visit_match_pat_tuple(state: _, v: &MatchPatTuple) {
fn visit_match_pat_tuple(state: _, v: &mut MatchPatTuple) {
let MatchPatTuple { paren_token: _, fields } = v;
for v in fields {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_enum_variant(state: _, v: &MatchPatEnumVariant) {
fn visit_match_pat_enum_variant(state: _, v: &mut MatchPatEnumVariant) {
let MatchPatEnumVariant {
match_span:_,
sim:_,
@ -95,7 +95,7 @@ visit_trait! {
state.visit_match_pat_simple(v);
}
}
fn visit_match_pat_simple(state: _, v: &MatchPatSimple) {
fn visit_match_pat_simple(state: _, v: &mut MatchPatSimple) {
match v {
MatchPatSimple::Paren(v) => state.visit_match_pat_paren_simple(v),
MatchPatSimple::Or(v) => state.visit_match_pat_or_simple(v),
@ -104,7 +104,7 @@ visit_trait! {
MatchPatSimple::Rest(v) => state.visit_match_pat_rest(v),
}
}
fn visit_match_pat(state: _, v: &MatchPat) {
fn visit_match_pat(state: _, v: &mut MatchPat) {
match v {
MatchPat::Simple(v) => state.visit_match_pat_simple(v),
MatchPat::Or(v) => state.visit_match_pat_or(v),
@ -118,13 +118,15 @@ visit_trait! {
with_debug_clone_and_fold! {
struct MatchPatBinding<> {
mutability: Option<Token![mut]>,
ident: Ident,
}
}
impl ToTokens for MatchPatBinding {
fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { ident } = self;
let Self { mutability, ident } = self;
mutability.to_tokens(tokens);
ident.to_tokens(tokens);
}
}
@ -211,12 +213,20 @@ impl ToTokens for MatchPatStructField {
colon_token,
pat,
} = self;
field_name.to_tokens(tokens);
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
if let (
None,
MatchPatSimple::Binding(MatchPatBinding {
mutability: _,
ident,
}),
) = (colon_token, pat)
{
if field_name == ident {
pat.to_tokens(tokens);
return;
}
}
field_name.to_tokens(tokens);
colon_token
.unwrap_or_else(|| Token![:](field_name.span()))
.to_tokens(tokens);
@ -450,7 +460,7 @@ trait ParseMatchPat: Sized {
Pat::Ident(PatIdent {
attrs: _,
by_ref,
mutability,
mut mutability,
ident,
subpat,
}) => {
@ -459,10 +469,13 @@ trait ParseMatchPat: Sized {
.errors
.error(by_ref, "ref not allowed in #[hdl] patterns");
}
if let Some(mutability) = mutability {
state
.errors
.error(mutability, "mut not allowed in #[hdl] patterns");
if let Some(mut_token) = mutability {
if state.sim.is_none() {
state
.errors
.error(mut_token, "mut not allowed in #[hdl] patterns");
mutability = None; // avoid duplicate errors
}
}
if let Some((at_token, _)) = subpat {
state
@ -474,18 +487,26 @@ trait ParseMatchPat: Sized {
variant_path,
enum_path,
variant_name,
}) => Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
),
}) => {
if let Some(mut_token) = mutability {
state
.errors
.error(mut_token, "mut not allowed on unit variants");
}
Self::enum_variant(
state,
MatchPatEnumVariant {
match_span: state.match_span,
sim: state.sim,
variant_path,
enum_path,
variant_name,
field: None,
},
)
}
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
mutability,
ident,
}))),
}
@ -980,15 +1001,16 @@ struct HdlMatchParseState<'a> {
struct HdlLetPatVisitState<'a> {
errors: &'a mut Errors,
bindings: BTreeSet<&'a Ident>,
bindings: BTreeMap<Ident, MatchPatBinding>,
}
impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
fn visit_match_pat_binding(&mut self, v: &'a MatchPatBinding) {
self.bindings.insert(&v.ident);
fn visit_match_pat_binding(&mut self, v: &'a mut MatchPatBinding) {
self.bindings.insert(v.ident.clone(), v.clone());
v.mutability = None;
}
fn visit_match_pat_or(&mut self, v: &'a MatchPatOr<MatchPat>) {
fn visit_match_pat_or(&mut self, v: &'a mut MatchPatOr<MatchPat>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
@ -998,7 +1020,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or(self, v);
}
fn visit_match_pat_or_simple(&mut self, v: &'a MatchPatOr<MatchPatSimple>) {
fn visit_match_pat_or_simple(&mut self, v: &'a mut MatchPatOr<MatchPatSimple>) {
if let Some(first_inner_vert) = v.first_inner_vert() {
self.errors.error(
first_inner_vert,
@ -1008,7 +1030,7 @@ impl<'a> VisitMatchPat<'a> for HdlLetPatVisitState<'a> {
visit_match_pat_or_simple(self, v);
}
fn visit_match_pat_enum_variant(&mut self, v: &'a MatchPatEnumVariant) {
fn visit_match_pat_enum_variant(&mut self, v: &'a mut MatchPatEnumVariant) {
self.errors.error(v, "refutable pattern in let statement");
}
}
@ -1048,7 +1070,7 @@ impl Visitor<'_> {
.error(else_, "#[hdl] let ... else { ... } is not implemented");
return empty_let();
}
let Ok(pat) = MatchPat::parse(
let Ok(mut pat) = MatchPat::parse(
&mut HdlMatchParseState {
sim,
match_span: span,
@ -1060,20 +1082,27 @@ impl Visitor<'_> {
};
let mut state = HdlLetPatVisitState {
errors: &mut self.errors,
bindings: BTreeSet::new(),
bindings: BTreeMap::new(),
};
state.visit_match_pat(&pat);
state.visit_match_pat(&mut pat);
let HdlLetPatVisitState {
errors: _,
bindings,
} = state;
let bindings_idents = bindings.keys();
let bindings = bindings.values();
let retval = if sim.is_some() {
parse_quote_spanned! {span=>
let (#(#bindings,)*) = {
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_value = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#let_token #pat #eq_token ::fayalite::sim::value::SimValue::into_value(__match_value) #semi_token
(#(#bindings,)*)
let __match_value = #expr;
let __match_value = {
use ::fayalite::sim::value::match_sim_value::*;
// use method syntax to deduce the correct trait to call
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
};
#let_token #pat #eq_token __match_value #semi_token
(#(#bindings_idents,)*)
};
}
} else {
@ -1105,7 +1134,7 @@ impl Visitor<'_> {
__match_variant,
);
#let_token #pat #eq_token __match_variant #semi_token
(#(#bindings,)* __scope,)
(#(#bindings_idents,)* __scope,)
};
}
};
@ -1142,8 +1171,13 @@ impl Visitor<'_> {
quote_spanned! {span=>
{
type __MatchTy<T> = <T as ::fayalite::ty::Type>::SimValue;
let __match_expr = ::fayalite::sim::value::ToSimValue::to_sim_value(&(#expr));
#match_token ::fayalite::sim::value::SimValue::into_value(__match_expr) {
let __match_value = #expr;
let __match_value = {
use ::fayalite::sim::value::match_sim_value::*;
// use method syntax to deduce the correct trait to call
::fayalite::sim::value::match_sim_value::MatchSimValueHelper::new(__match_value).__fayalite_match_sim_value()
};
#match_token __match_value {
#(#arms)*
}
}

View file

@ -12,7 +12,7 @@ fn blinky(platform_io_builder: PlatformIOBuilder<'_>) {
clk: clk_input.clk,
rst,
};
let max_value = (Expr::ty(clk_input).frequency() / 2.0).round_ties_even() as u64 - 1;
let max_value = (clk_input.ty().frequency() / 2.0).round_ties_even() as u64 - 1;
let int_ty = UInt::range_inclusive(0..=max_value);
#[hdl]
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));

View file

@ -105,7 +105,7 @@ fn tx_only_uart(
connect(tx, tx_bits[uart_state_reg]);
#[hdl]
if uart_state_reg.cmp_eq(Expr::ty(tx_bits).len() - 1) {
if uart_state_reg.cmp_eq(tx_bits.ty().len() - 1) {
connect(next_uart_state, 0_hdl_u4);
let next_addr_val = addr_reg + 1u8;
#[hdl]

View file

@ -31,3 +31,81 @@
//! }
//! }
//! ```
//!
//! You can also use `#[hdl(sim)] let` to destructure [`SimValue`]s (or anything that implements [`ToSimValue`]).
//!
//! [`SimValue`]: crate::sim::value::SimValue
//! [`ToSimValue`]: crate::sim::value::ToSimValue
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! struct MyStruct<T> {
//! a: UInt<8>,
//! b: Bool,
//! c: T,
//! }
//!
//! #[hdl]
//! fn destructure<T: Type>(v: SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! mut b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! *b = false; // can modify b since mut was used
//! }
//!
//! #[hdl]
//! fn destructure_ref<'a, T: Type>(v: &'a SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: &'a SimValue<UInt<8>> = a;
//! let _: &'a SimValue<Bool> = b;
//! let _: &'a SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_mut<'a, T: Type>(v: &'a mut SimValue<MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! **b = true; // you can modify v by modifying b which borrows from it
//!
//! // that gives these types:
//! let _: &'a mut SimValue<UInt<8>> = a;
//! let _: &'a mut SimValue<Bool> = b;
//! let _: &'a mut SimValue<T> = c;
//! }
//!
//! #[hdl]
//! fn destructure_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyStruct<T>>) {
//! #[hdl(sim)]
//! let MyStruct::<T> {
//! a,
//! b,
//! c,
//! } = v;
//!
//! // that gives these types:
//! let _: SimValue<UInt<8>> = a;
//! let _: SimValue<Bool> = b;
//! let _: SimValue<T> = c;
//! }
//! ```

View file

@ -9,3 +9,78 @@
//!
//! `#[hdl] match` statements can only match one level of struct/tuple/enum pattern for now,
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
//!
//! You can also use `#[hdl(sim)] match` to match [`SimValue`]s (or anything that implements [`ToSimValue`]).
//!
//! `#[hdl(sim)] match` statements' bodies may have any type, unlike `#[hdl] match`.
//!
//! [`SimValue`]: crate::sim::value::SimValue
//! [`ToSimValue`]: crate::sim::value::ToSimValue
//!
//! ```
//! # use fayalite::prelude::*;
//! #[hdl]
//! enum MyEnum<T> {
//! A,
//! B(Bool),
//! C(T),
//! }
//!
//! #[hdl]
//! fn match_move<T: Type>(v: SimValue<MyEnum<T>>) -> String {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => String::from("got A"),
//! MyEnum::<T>::B(mut b) => {
//! let _: SimValue<Bool> = b; // b has this type
//! let text = format!("got B({b})");
//! *b = true; // can modify b since mut was used
//! text
//! }
//! _ => String::from("something else"),
//! }
//! }
//!
//! #[hdl]
//! fn match_ref<'a, T: Type>(v: &'a SimValue<MyEnum<T>>) -> u32 {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => 1,
//! MyEnum::<T>::B(b) => {
//! let _: &'a SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! 5
//! }
//! _ => 42,
//! }
//! }
//!
//! #[hdl]
//! fn match_mut<'a, T: Type>(v: &'a mut SimValue<MyEnum<T>>) -> Option<&'a mut SimValue<T>> {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => None,
//! MyEnum::<T>::B(b) => {
//! println!("got B({b})");
//! **b = true; // you can modify v by modifying b which borrows from it
//! let _: &'a mut SimValue<Bool> = b; // b has this type
//! None
//! }
//! MyEnum::<T>::C(v) => Some(v), // you can return matched values
//! _ => None, // HDL enums can have invalid discriminants, so we need this extra match arm
//! }
//! }
//!
//! #[hdl]
//! fn match_to_sim_value<'a, T: Type>(v: impl ToSimValue<Type = MyEnum<T>>) {
//! #[hdl(sim)]
//! match v {
//! MyEnum::<T>::A => println!("got A"),
//! MyEnum::<T>::B(b) => {
//! let _: SimValue<Bool> = b; // b has this type
//! println!("got B({b})");
//! }
//! _ => println!("something else"),
//! }
//! }
//! ```

View file

@ -3,13 +3,13 @@
use crate::{
expr::{
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator, ExprPartialEq},
CastToBits, Expr, HdlPartialEq, HdlPartialEqImpl, ReduceBits, ToExpr, ValueType, Valueless,
ops::{ArrayLiteral, ExprFromIterator, ExprIntoIterator},
},
int::{Bool, DYN_SIZE, DynSize, KnownSize, Size, SizeType},
intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor},
sim::value::{SimValue, SimValuePartialEq},
sim::value::SimValue,
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter,
@ -19,7 +19,7 @@ use crate::{
util::ConstUsize,
};
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
use std::{iter::FusedIterator, ops::Index};
use std::{borrow::Cow, iter::FusedIterator, ops::Index};
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
@ -221,7 +221,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
let value = AsMut::<[SimValue<T>]>::as_mut(value);
assert_eq!(self.len(), value.len());
for element_value in value {
assert_eq!(SimValue::ty(element_value), element_ty);
assert_eq!(element_value.ty(), element_ty);
let (element_opaque, rest) = opaque.split_at(element_size);
SimValue::opaque_mut(element_value).clone_from_slice(element_opaque);
opaque = rest;
@ -238,7 +238,7 @@ impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
let value = AsRef::<[SimValue<T>]>::as_ref(value);
assert_eq!(self.len(), value.len());
for element_value in value {
assert_eq!(SimValue::ty(element_value), element_ty);
assert_eq!(element_value.ty(), element_ty);
writer.fill_prefix_with(element_size, |writer| {
writer.fill_cloned_from_slice(SimValue::opaque(element_value).as_slice())
});
@ -326,14 +326,30 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
impl<Lhs: Type, Rhs: Type, Len: Size> HdlPartialEqImpl<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: ExprPartialEq<Rhs>,
Lhs: HdlPartialEqImpl<Rhs>,
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
fn cmp_value_eq(
lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
rhs: ArrayType<Rhs, Len>,
rhs_value: Cow<'_, <ArrayType<Rhs, Len> as Type>::SimValue>,
) -> bool {
assert_eq!(lhs.len(), rhs.len());
let lhs = lhs.element();
let rhs = rhs.element();
let lhs_value: &[_] = (*lhs_value).as_ref();
let rhs_value: &[_] = (*rhs_value).as_ref();
for (lhs_value, rhs_value) in lhs_value.iter().zip(rhs_value) {
if !Lhs::cmp_value_eq(lhs, Cow::Borrowed(lhs_value), rhs, Cow::Borrowed(rhs_value)) {
return false;
}
}
true
}
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
lhs.into_iter()
.zip(rhs)
.map(|(l, r)| l.cmp_eq(r))
@ -341,11 +357,8 @@ where
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
let lhs_ty = Expr::ty(lhs);
let rhs_ty = Expr::ty(rhs);
assert_eq!(lhs_ty.len(), rhs_ty.len());
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<ArrayType<Rhs, Len>>) -> Expr<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
lhs.into_iter()
.zip(rhs)
.map(|(l, r)| l.cmp_ne(r))
@ -353,17 +366,19 @@ where
.cast_to_bits()
.any_one_bits()
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> SimValuePartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: SimValuePartialEq<Rhs>,
{
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<ArrayType<Rhs, Len>>) -> bool {
AsRef::<[_]>::as_ref(&**this)
.iter()
.zip(AsRef::<[_]>::as_ref(&**other))
.all(|(l, r)| SimValuePartialEq::sim_value_eq(l, r))
fn cmp_valueless_eq(
lhs: Valueless<Self>,
rhs: Valueless<ArrayType<Rhs, Len>>,
) -> Valueless<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
Valueless::new(Bool)
}
fn cmp_valueless_ne(
lhs: Valueless<Self>,
rhs: Valueless<ArrayType<Rhs, Len>>,
) -> Valueless<Bool> {
assert_eq!(lhs.ty().len(), rhs.ty().len());
Valueless::new(Bool)
}
}
@ -374,7 +389,7 @@ impl<T: Type, Len: Size> ExprIntoIterator for ArrayType<T, Len> {
fn expr_into_iter(e: Expr<Self>) -> Self::ExprIntoIter {
ExprArrayIter {
base: e,
indexes: 0..Expr::ty(e).len(),
indexes: 0..e.ty().len(),
}
}
}

View file

@ -3,12 +3,14 @@
use crate::{
expr::{
CastToBits, Expr, ReduceBits, ToExpr,
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
CastToBits, Expr, HdlPartialEqImpl, ReduceBits, ToExpr, ToSimValueInner, ValueType,
Valueless,
ops::{ArrayLiteral, BundleLiteral},
value_category::{ValueCategoryCommon, ValueCategoryExpr, ValueCategoryValue},
},
int::{Bool, DynSize},
intern::{Intern, InternSlice, Interned},
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
sim::value::{SimValue, SimValueEq, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize,
@ -18,7 +20,7 @@ use crate::{
util::HashMap,
};
use serde::{Deserialize, Serialize};
use std::{fmt, marker::PhantomData};
use std::{borrow::Cow, fmt, marker::PhantomData};
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub struct BundleField {
@ -317,7 +319,7 @@ impl<'a> BundleSimValueFromOpaque<'a> {
#[track_caller]
pub fn field_clone_from_opaque<T: Type>(&mut self, field_value: &mut SimValue<T>) {
let (field_ty, field_opaque) = self.field_ty_and_opaque::<T>();
assert_eq!(field_ty, SimValue::ty(field_value));
assert_eq!(field_ty, field_value.ty());
SimValue::opaque_mut(field_value).clone_from_slice(field_opaque);
}
}
@ -353,7 +355,7 @@ impl<'a> BundleSimValueToOpaque<'a> {
else {
panic!("tried to write too many fields with BundleSimValueToOpaque");
};
assert_eq!(T::from_canonical(ty), SimValue::ty(field_value));
assert_eq!(T::from_canonical(ty), field_value.ty());
self.writer.fill_prefix_with(ty.size(), |writer| {
writer.fill_cloned_from_slice(SimValue::opaque(field_value).as_slice())
});
@ -566,23 +568,54 @@ macro_rules! impl_tuples {
builder.finish()
};
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*) {
impl<'a, $($T: ToSimValue,)*> ToSimValueInner<'a> for ($($T,)*)
where
Self: ValueType<Type = ($($T::Type,)*)>,
{
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
let ($($var,)*) = this;
Cow::Owned(($($var.to_sim_value(),)*))
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
let ($($var,)*) = this;
Cow::Owned(($($var.into_sim_value(),)*))
}
}
impl<$($T: ValueType,)*> ValueType for ($($T,)*)
where
ValueCategoryValue: ValueCategoryCommon<($($T::ValueCategory,)*)>,
{
type Type = ($($T::Type,)*);
type ValueCategory = <ValueCategoryValue as ValueCategoryCommon<($($T::ValueCategory,)*)>>::Common;
fn ty(&self) -> Self::Type {
let ($($var,)*) = self;
($($var.ty(),)*)
}
}
impl<$($T: ToExpr,)*> ToExpr for ($($T,)*)
where
Self: ValueType<Type = ($($T::Type,)*)>,
{
fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_expr();)*
let ty = ($(Expr::ty($var),)*);
let ty = ($($var.ty(),)*);
let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
}
}
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
impl<$($T: Type,)*> ValueType for TupleBuilder<($(Expr<$T>,)*)> {
type Type = ($($T,)*);
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
let ($($var,)*) = self.0;
($($var.ty(),)*)
}
}
impl<$($T: Type,)*> ToExpr for TupleBuilder<($(Expr<$T>,)*)> {
fn to_expr(&self) -> Expr<Self::Type> {
let ($($var,)*) = self.0;
let ty = ($(Expr::ty($var),)*);
let ty = ($($var.ty(),)*);
let field_values = [$(Expr::canonical($var)),*];
BundleLiteral::new(ty, field_values.intern_slice()).to_expr()
}
@ -618,7 +651,7 @@ macro_rules! impl_tuples {
};
let mut opaque = OpaqueSimValue::empty();
$(let $var = $var.into_sim_value_with_type($ty_var.ty);
assert_eq!(SimValue::ty(&$var), $ty_var.ty);
assert_eq!($var.ty(), $ty_var.ty);
opaque.extend_from_slice(SimValue::opaque(&$var).as_slice());
)*
SimValue::from_opaque(ty, opaque)
@ -640,53 +673,82 @@ macro_rules! impl_tuples {
SimValue::from_value(ty, ($($var,)*))
}
}
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*) {
type Type = ($($T::Type,)*);
impl<$($T: ToSimValue,)*> ToSimValue for ($($T,)*)
where
Self: ValueType<Type = ($($T::Type,)*)>,
{
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)*
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
}
#[track_caller]
fn into_sim_value(self) -> SimValue<Self::Type> {
let ($($var,)*) = self;
$(let $var = $var.to_sim_value();)*
SimValue::from_value(($(SimValue::ty(&$var),)*), ($($var,)*))
SimValue::from_value(($($var.ty(),)*), ($($var,)*))
}
}
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
impl<$($Lhs: Type + HdlPartialEqImpl<$Rhs>, $Rhs: Type,)*> HdlPartialEqImpl<($($Rhs,)*)> for ($($Lhs,)*) {
#[track_caller]
fn cmp_value_eq(
lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
rhs: ($($Rhs,)*),
rhs_value: Cow<'_, <($($Rhs,)*) as Type>::SimValue>,
) -> bool {
#![allow(unused_variables)]
let ($($lhs_var,)*) = &*lhs_value;
let ($($rhs_var,)*) = &*rhs_value;
let retval = true;
$(let retval = retval && $Lhs::cmp_value_eq(lhs.$num, Cow::Borrowed($lhs_var), rhs.$num, Cow::Borrowed($rhs_var));)*
retval
}
#[track_caller]
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_eq($lhs_var, $rhs_var)),)*]),
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_eq($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.all_one_bits()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
#[track_caller]
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<($($Rhs,)*)>) -> Expr<Bool> {
let ($($lhs_var,)*) = *lhs;
let ($($rhs_var,)*) = *rhs;
ArrayLiteral::<Bool, DynSize>::new(
Bool,
FromIterator::from_iter([$(Expr::canonical(ExprPartialEq::cmp_ne($lhs_var, $rhs_var)),)*]),
FromIterator::from_iter([$(Expr::canonical($Lhs::cmp_expr_ne($lhs_var, $rhs_var)),)*]),
)
.cast_to_bits()
.any_one_bits()
}
}
impl<$($Lhs: SimValuePartialEq<$Rhs>, $Rhs: Type,)*> SimValuePartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn sim_value_eq(lhs: &SimValue<Self>, rhs: &SimValue<($($Rhs,)*)>) -> bool {
let ($($lhs_var,)*) = &**lhs;
let ($($rhs_var,)*) = &**rhs;
let retval = true;
$(let retval = retval && $lhs_var == $rhs_var;)*
retval
#[track_caller]
fn cmp_valueless_eq(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
let ($($lhs_var,)*) = lhs.ty();
let ($($rhs_var,)*) = rhs.ty();
// let them check that the types can be compared
$($Lhs::cmp_valueless_eq(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
Valueless::new(Bool)
}
#[track_caller]
fn cmp_valueless_ne(lhs: Valueless<Self>, rhs: Valueless<($($Rhs,)*)>) -> Valueless<Bool> {
let ($($lhs_var,)*) = lhs.ty();
let ($($rhs_var,)*) = rhs.ty();
// let them check that the types can be compared
$($Lhs::cmp_valueless_ne(Valueless::new($lhs_var), Valueless::new($rhs_var));)*
Valueless::new(Bool)
}
}
impl<$($T: SimValueEq + HdlPartialEqImpl<$T>,)*> SimValueEq for ($($T,)*) {}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
@ -775,9 +837,16 @@ impl<T: ?Sized + Send + Sync + 'static> Default for PhantomDataBuilder<T> {
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomDataBuilder<T> {
type Type = PhantomData<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomDataBuilder<T> {
fn to_expr(&self) -> Expr<Self::Type> {
PhantomData.to_expr()
}
@ -803,17 +872,22 @@ impl<T: ?Sized + Send + Sync + 'static> StaticType for PhantomData<T> {
const MASK_TYPE_PROPERTIES: TypeProperties = <()>::TYPE_PROPERTIES;
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
impl<T: ?Sized + Send + Sync + 'static> ValueType for PhantomData<T> {
type Type = PhantomData<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
PhantomData
}
}
impl<T: ?Sized + Send + Sync + 'static> ToExpr for PhantomData<T> {
fn to_expr(&self) -> Expr<Self::Type> {
BundleLiteral::new(PhantomData, Interned::default()).to_expr()
}
}
impl<T: ?Sized + Send + Sync + 'static> ToSimValue for PhantomData<T> {
type Type = PhantomData<T>;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self> {
SimValue::from_value(*self, *self)

View file

@ -1,10 +1,11 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{Expr, ToExpr},
expr::{Expr, ValueType},
hdl,
int::Bool,
reset::{Reset, ResetType},
sim::value::SimValue,
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
@ -91,29 +92,34 @@ impl StaticType for Clock {
}
pub trait ToClock {
fn to_clock(&self) -> Expr<Clock>;
type Output: ValueType<Type = Clock>;
fn to_clock(&self) -> Self::Output;
}
impl<T: ?Sized + ToClock> ToClock for &'_ T {
fn to_clock(&self) -> Expr<Clock> {
type Output = T::Output;
fn to_clock(&self) -> Self::Output {
(**self).to_clock()
}
}
impl<T: ?Sized + ToClock> ToClock for &'_ mut T {
fn to_clock(&self) -> Expr<Clock> {
type Output = T::Output;
fn to_clock(&self) -> Self::Output {
(**self).to_clock()
}
}
impl<T: ?Sized + ToClock> ToClock for Box<T> {
fn to_clock(&self) -> Expr<Clock> {
type Output = T::Output;
fn to_clock(&self) -> Self::Output {
(**self).to_clock()
}
}
impl ToClock for Expr<Clock> {
fn to_clock(&self) -> Expr<Clock> {
type Output = Expr<Clock>;
fn to_clock(&self) -> Self::Output {
*self
}
}
@ -125,7 +131,25 @@ pub struct ClockDomain<R: ResetType = Reset> {
}
impl ToClock for bool {
fn to_clock(&self) -> Expr<Clock> {
self.to_expr().to_clock()
type Output = SimValue<Clock>;
fn to_clock(&self) -> Self::Output {
SimValue::from_value(Clock, *self)
}
}
impl ToClock for SimValue<Bool> {
type Output = SimValue<Clock>;
fn to_clock(&self) -> Self::Output {
SimValue::from_value(Clock, **self)
}
}
impl ToClock for SimValue<Clock> {
type Output = SimValue<Clock>;
fn to_clock(&self) -> Self::Output {
self.clone()
}
}

View file

@ -2,10 +2,7 @@
// See Notices.txt for copyright information
use crate::{
expr::{
Expr, ToExpr,
ops::{ExprPartialEq, VariantAccess},
},
expr::{Expr, HdlPartialEq, HdlPartialEqImpl, ToExpr, ValueType, ops::VariantAccess},
hdl,
int::{Bool, UIntValue},
intern::{Intern, Interned},
@ -13,7 +10,7 @@ use crate::{
EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope, connect,
enum_match_variants_helper, incomplete_wire, wire,
},
sim::value::{SimValue, SimValuePartialEq},
sim::value::SimValue,
source_location::SourceLocation,
ty::{
CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize,
@ -24,7 +21,7 @@ use crate::{
};
use bitvec::{order::Lsb0, slice::BitSlice, view::BitView};
use serde::{Deserialize, Serialize};
use std::{convert::Infallible, fmt, iter::FusedIterator, sync::Arc};
use std::{borrow::Cow, convert::Infallible, fmt, iter::FusedIterator, sync::Arc};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct EnumVariant {
@ -599,7 +596,7 @@ impl<'a> EnumSimValueFromOpaque<'a> {
let (Some(variant_ty), variant_bits, padding_bits) = self.known_variant(true) else {
self.usage_error(true);
};
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
assert_eq!(value.ty(), T::from_canonical(variant_ty));
SimValue::bits_mut(value)
.bits_mut()
.copy_from_bitslice(variant_bits);
@ -711,7 +708,7 @@ impl<'a> EnumSimValueToOpaque<'a> {
let Some(variant_ty) = self.variants[discriminant].ty else {
panic!("expected variant to have no field");
};
assert_eq!(SimValue::ty(value), T::from_canonical(variant_ty));
assert_eq!(value.ty(), T::from_canonical(variant_ty));
self.known_variant(discriminant, Some(SimValue::opaque(value)), padding)
}
}
@ -732,9 +729,38 @@ pub enum HdlOption<T: Type> {
HdlSome(T),
}
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
impl<Lhs: Type + HdlPartialEqImpl<Rhs>, Rhs: Type> HdlPartialEqImpl<HdlOption<Rhs>>
for HdlOption<Lhs>
{
fn cmp_value_eq(
lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
rhs: HdlOption<Rhs>,
rhs_value: Cow<'_, <HdlOption<Rhs> as Type>::SimValue>,
) -> bool {
type SimValueMatch<T> = <T as Type>::SimValue;
match (&*lhs_value, &*rhs_value) {
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
true
}
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
false
}
(
SimValueMatch::<Self>::HdlSome(l, _),
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
) => HdlPartialEqImpl::cmp_value_eq(
lhs.HdlSome,
Cow::Borrowed(&**l),
rhs.HdlSome,
Cow::Borrowed(&**r),
),
}
}
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
@ -743,7 +769,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlSome(rhs) => connect(cmp_eq, lhs.cmp_eq(rhs)),
HdlNone => connect(cmp_eq, false),
}
}
@ -760,7 +786,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
@ -769,7 +795,7 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlSome(rhs) => connect(cmp_ne, lhs.cmp_ne(rhs)),
HdlNone => connect(cmp_ne, true),
}
}
@ -786,25 +812,6 @@ impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> fo
}
}
impl<Lhs: SimValuePartialEq<Rhs>, Rhs: Type> SimValuePartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<HdlOption<Rhs>>) -> bool {
type SimValueMatch<T> = <T as Type>::SimValue;
match (&**this, &**other) {
(SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_)) => {
true
}
(SimValueMatch::<Self>::HdlSome(..), SimValueMatch::<HdlOption<Rhs>>::HdlNone(_))
| (SimValueMatch::<Self>::HdlNone(_), SimValueMatch::<HdlOption<Rhs>>::HdlSome(..)) => {
false
}
(
SimValueMatch::<Self>::HdlSome(l, _),
SimValueMatch::<HdlOption<Rhs>>::HdlSome(r, _),
) => l == r,
}
}
}
#[allow(non_snake_case)]
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
HdlOption[T::TYPE].HdlNone()
@ -813,7 +820,7 @@ pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
#[allow(non_snake_case)]
pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
let value = value.to_expr();
HdlOption[Expr::ty(value)].HdlSome(value)
HdlOption[value.ty()].HdlSome(value)
}
impl<T: Type> HdlOption<T> {
@ -853,7 +860,7 @@ impl<T: Type> HdlOption<T> {
let value = f(value).inspect_err(|_| {
and_then_out.complete(()); // avoid error
})?;
let and_then_out = and_then_out.complete(Expr::ty(value));
let and_then_out = and_then_out.complete(value.ty());
connect(and_then_out, value);
drop(some_scope);
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
@ -861,7 +868,7 @@ impl<T: Type> HdlOption<T> {
else {
unreachable!();
};
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
connect(and_then_out, and_then_out.ty().HdlNone());
drop(none_scope);
Ok(and_then_out)
}
@ -879,8 +886,8 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
#[hdl]
let and_out = wire(Expr::ty(opt_b));
connect(and_out, Expr::ty(opt_b).HdlNone());
let and_out = wire(opt_b.ty());
connect(and_out, opt_b.ty().HdlNone());
#[hdl]
if let HdlSome(_) = expr {
connect(and_out, opt_b);
@ -894,8 +901,8 @@ impl<T: Type> HdlOption<T> {
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
) -> Result<Expr<Self>, E> {
#[hdl]
let filtered = wire(Expr::ty(expr));
connect(filtered, Expr::ty(expr).HdlNone());
let filtered = wire(expr.ty());
connect(filtered, expr.ty().HdlNone());
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
@ -969,7 +976,7 @@ impl<T: Type> HdlOption<T> {
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> {
#[hdl]
let mapped = wire(Expr::ty(default));
let mapped = wire(default.ty());
let mut f = Some(f);
#[hdl]
match expr {
@ -994,12 +1001,12 @@ impl<T: Type> HdlOption<T> {
match expr {
HdlSome(v) => {
let v = f.take().unwrap()(v);
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
connect(mapped, v);
}
HdlNone => {
let v = default.take().unwrap()();
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
let mapped = *retval.get_or_insert_with(|| mapped.complete(v.ty()));
connect(mapped, v);
}
}
@ -1009,7 +1016,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let or_out = wire(Expr::ty(expr));
let or_out = wire(expr.ty());
connect(or_out, opt_b);
#[hdl]
if let HdlSome(_) = expr {
@ -1021,7 +1028,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
#[hdl]
let or_else_out = wire(Expr::ty(expr));
let or_else_out = wire(expr.ty());
connect(or_else_out, f());
#[hdl]
if let HdlSome(_) = expr {
@ -1033,7 +1040,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(default));
let unwrap_or_else_out = wire(default.ty());
connect(unwrap_or_else_out, default);
#[hdl]
if let HdlSome(v) = expr {
@ -1045,7 +1052,7 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
let unwrap_or_else_out = wire(expr.ty().HdlSome);
connect(unwrap_or_else_out, f());
#[hdl]
if let HdlSome(v) = expr {
@ -1057,14 +1064,14 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let xor_out = wire(Expr::ty(expr));
let xor_out = wire(expr.ty());
#[hdl]
if let HdlSome(_) = expr {
#[hdl]
if let HdlNone = opt_b {
connect(xor_out, expr);
} else {
connect(xor_out, Expr::ty(expr).HdlNone());
connect(xor_out, expr.ty().HdlNone());
}
} else {
connect(xor_out, opt_b);
@ -1075,8 +1082,8 @@ impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
#[hdl]
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
connect(zip_out, Expr::ty(zip_out).HdlNone());
let zip_out = wire(HdlOption[(expr.ty().HdlSome, other.ty().HdlSome)]);
connect(zip_out, zip_out.ty().HdlNone());
#[hdl]
if let HdlSome(l) = expr {
#[hdl]
@ -1093,11 +1100,11 @@ impl<T: Type> HdlOption<HdlOption<T>> {
#[track_caller]
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
#[hdl]
let flattened = wire(Expr::ty(expr).HdlSome);
let flattened = wire(expr.ty().HdlSome);
#[hdl]
match expr {
HdlSome(v) => connect(flattened, v),
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
HdlNone => connect(flattened, expr.ty().HdlSome.HdlNone()),
}
flattened
}
@ -1107,7 +1114,7 @@ impl<T: Type, U: Type> HdlOption<(T, U)> {
#[hdl]
#[track_caller]
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
let (t, u) = Expr::ty(expr).HdlSome;
let (t, u) = expr.ty().HdlSome;
#[hdl]
let unzipped = wire((HdlOption[t], HdlOption[u]));
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,222 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{expr::Valueless, expr::ops::make_impls, prelude::*};
use std::num::NonZero;
macro_rules! assert_neg_impls {
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
const _: () = {
fn _check_neg_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
-> impl ValueType<
ValueCategory = <$ty as ValueType>::ValueCategory,
Type = <<Valueless<<$ty as ValueType>::Type> as std::ops::Neg>::Output as ValueType>::Type,
> {
std::ops::Neg::neg(v)
}
};
};
}
make_impls! {
#[kinds((sint_local<'a, Width>))]
assert_neg_impls! {}
}
macro_rules! assert_not_impls {
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
const _: () = {
fn _check_not_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
-> impl ValueType<
ValueCategory = <$ty as ValueType>::ValueCategory,
Type = <<Valueless<<$ty as ValueType>::Type> as std::ops::Not>::Output as ValueType>::Type,
> {
std::ops::Not::not(v)
}
};
};
}
make_impls! {
#[kinds((int_local<'a, Width>), (bool<'a>))]
assert_not_impls! {}
}
macro_rules! assert_cast_to_bits_impls {
([$($Lifetimes:tt)*][$($Bounds:tt)*] ($ty:ty),) => {
const _: () = {
fn _check_cast_to_bits_impl<$($Lifetimes)*$($Bounds)*>(v: $ty)
-> impl ValueType<
ValueCategory = <$ty as ValueType>::ValueCategory,
Type = <<Valueless<<$ty as ValueType>::Type> as CastToBits>::Output as ValueType>::Type,
> {
<$ty as CastToBits>::cast_to_bits(&v)
}
};
};
}
make_impls! {
#[kinds((uint<'a, Width>), (sint<'a, Width>), (bool<'a>))]
assert_cast_to_bits_impls! {}
}
macro_rules! assert_simple_bin_op_impls {
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), !$Trait:ident::$f:ident) => {
#[expect(dead_code)]
const _: () = {
trait HasImpl {
fn check_impl(self);
}
trait NoImpl {
fn check_impl(&self) -> &'static dyn NoImpl;
}
impl<L, R> NoImpl for (L, R) {
fn check_impl(&self) -> &'static dyn NoImpl {
unreachable!()
}
}
impl<L: std::ops::$Trait<R>, R> HasImpl for (L, R) {
fn check_impl(self) {}
}
fn check_simple_bin_op_no_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*) {
let _: &'static dyn NoImpl = (l, r).check_impl();
}
};
};
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shl::shl) => {
const _: () = {
#[expect(dead_code)]
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize)
-> impl ValueType<
ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory,
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shl<usize>>::Output as ValueType>::Type,
> {
std::ops::Shl::shl(l, r)
}
};
};
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] (usize), Shr::shr) => {
const _: () = {
#[expect(dead_code)]
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: usize)
-> impl ValueType<
ValueCategory = <($($L)*, usize) as ValueType>::ValueCategory,
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::Shr<usize>>::Output as ValueType>::Type,
> {
std::ops::Shr::shr(l, r)
}
};
};
([$($LLifetimes:tt)*][$($LBounds:tt)*] ($($L:tt)*), [$($RLifetimes:tt)*][$($RBounds:tt)*] ($($R:tt)*), $Trait:ident::$f:ident) => {
const _: () = {
#[expect(dead_code)]
fn check_simple_bin_op_impl<$($LLifetimes)* $($RLifetimes)* $($LBounds)* $($RBounds)*>(l: $($L)*, r: $($R)*)
-> impl ValueType<
ValueCategory = <($($L)*, $($R)*) as ValueType>::ValueCategory,
Type = <<Valueless<<$($L)* as ValueType>::Type> as std::ops::$Trait<Valueless<<$($R)* as ValueType>::Type>>>::Output as ValueType>::Type,
> {
std::ops::$Trait::$f(l, r)
}
};
};
($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), !$FirstTrait:ident::$first_f:ident, $($rest:tt)*) => {
assert_simple_bin_op_impls! {
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), !$FirstTrait::$first_f
}
assert_simple_bin_op_impls! {
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)*
}
};
($LLifetimes:tt $LBounds:tt ($($L:tt)*), $RLifetimes:tt $RBounds:tt ($($R:tt)*), $FirstTrait:ident::$first_f:ident, $($rest:tt)*) => {
assert_simple_bin_op_impls! {
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $FirstTrait::$first_f
}
assert_simple_bin_op_impls! {
$LLifetimes $LBounds ($($L)*), $RLifetimes $RBounds ($($R)*), $($rest)*
}
};
}
make_impls! {
#[kinds((uint_local<'l, L>))]
#[kinds((uint<'r, R>))]
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((uint<'l, L>))]
#[kinds((uint_local<'r, R>))]
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor, Shl::shl, Shr::shr}
}
make_impls! {
#[kinds((sint_local<'l, L>))]
#[kinds((sint<'r, R>))]
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((sint<'l, L>))]
#[kinds((sint_local<'r, R>))]
assert_simple_bin_op_impls! {Add::add, Sub::sub, Mul::mul, Div::div, Rem::rem, BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((sint<'l, L>))]
#[kinds((uint_local<'r, R>))]
assert_simple_bin_op_impls! {Shl::shl, Shr::shr}
}
make_impls! {
#[kinds((uint_local<'l, L>))]
assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr}
}
make_impls! {
#[kinds((sint_local<'l, L>))]
assert_simple_bin_op_impls! {[][] (usize), Shl::shl, Shr::shr}
}
make_impls! {
#[kinds((bool_local<'l>))]
#[kinds((bool_local<'r>))]
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((bool<'l>))]
#[kinds((Valueless<Bool>))]
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((Valueless<Bool>))]
#[kinds((bool<'r>))]
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
make_impls! {
#[kinds((bool_at_most_sim_value<'l>))]
#[kinds((bool_at_most_sim_value<'r>))]
assert_simple_bin_op_impls! {BitAnd::bitand, BitOr::bitor, BitXor::bitxor}
}
// assert there are no impls of BitAnd/BitOr/BitXor between Expr<Bool> and Rust's bool,
// since that helps avoid using `==`/`!=` in hdl boolean expressions, which doesn't do
// what is usually wanted.
make_impls! {
#[kinds((Expr<Bool>))]
#[kinds(bool)]
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
}
make_impls! {
#[kinds(bool)]
#[kinds((Expr<Bool>))]
assert_simple_bin_op_impls! {!BitAnd::bitand, !BitOr::bitor, !BitXor::bitxor}
}

View file

@ -3,7 +3,7 @@
use crate::{
array::Array,
bundle::{Bundle, BundleField},
expr::{Expr, Flow, ToExpr},
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
intern::{Intern, Interned},
memory::{DynPortType, MemPort},
module::{Instance, ModuleIO, TargetName},
@ -196,9 +196,18 @@ macro_rules! impl_target_base {
}
}
impl ToExpr for $TargetBase {
impl ValueType for $TargetBase {
type Type = CanonicalType;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
match self {
$(Self::$Variant(v) => v.ty().canonical(),)*
}
}
}
impl ToExpr for $TargetBase {
fn to_expr(&self) -> Expr<Self::Type> {
match self {
$(Self::$Variant(v) => Expr::canonical(v.to_expr()),)*

View file

@ -0,0 +1,378 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{expr::ValueType, ty::Type};
trait ValueCategorySealed {}
#[expect(private_bounds)]
pub trait ValueCategory:
ValueCategorySealed
+ Copy
+ Ord
+ Default
+ std::fmt::Debug
+ std::hash::Hash
+ 'static
+ Send
+ Sync
{
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>>: ValueType<Type = D::Type, ValueCategory: ValueCategoryIsValueSimValueExprOrValueless>;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D>;
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D>;
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
pub struct ValueCategoryValue;
impl ValueCategorySealed for ValueCategoryValue {}
impl ValueCategory for ValueCategoryValue {
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Value;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_value()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
ValueCategoryDispatchOutputEnum::Value(dispatch.dispatch_value())
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
pub struct ValueCategorySimValue;
impl ValueCategorySealed for ValueCategorySimValue {}
impl ValueCategory for ValueCategorySimValue {
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::SimValue;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_sim_value()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
ValueCategoryDispatchOutputEnum::SimValue(dispatch.dispatch_sim_value())
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
pub struct ValueCategoryExpr;
impl ValueCategorySealed for ValueCategoryExpr {}
impl ValueCategory for ValueCategoryExpr {
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Expr;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_expr()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
ValueCategoryDispatchOutputEnum::Expr(dispatch.dispatch_expr())
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Debug, Hash)]
pub struct ValueCategoryValueless;
impl ValueCategorySealed for ValueCategoryValueless {}
impl ValueCategory for ValueCategoryValueless {
type DispatchOutput<D: ValueCategoryDispatch<InputValueCategory = Self>> = D::Valueless;
#[track_caller]
fn dispatch<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> Self::DispatchOutput<D> {
dispatch.dispatch_valueless()
}
#[track_caller]
fn dispatch_enum<D: ValueCategoryDispatch<InputValueCategory = Self>>(
dispatch: D,
) -> ValueCategoryDispatchOutputEnum<D> {
ValueCategoryDispatchOutputEnum::Valueless(dispatch.dispatch_valueless())
}
}
pub trait ValueCategoryIsValue: ValueCategoryIsValueOrSimValue {}
impl ValueCategoryIsValue for ValueCategoryValue {}
pub trait ValueCategoryIsValueOrSimValue: ValueCategoryIsValueSimValueOrExpr {}
impl ValueCategoryIsValueOrSimValue for ValueCategoryValue {}
impl ValueCategoryIsValueOrSimValue for ValueCategorySimValue {}
pub trait ValueCategoryIsValueSimValueOrExpr: ValueCategoryIsValueSimValueExprOrValueless {}
impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryValue {}
impl ValueCategoryIsValueSimValueOrExpr for ValueCategorySimValue {}
impl ValueCategoryIsValueSimValueOrExpr for ValueCategoryExpr {}
pub trait ValueCategoryIsValueSimValueExprOrValueless: ValueCategory {}
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValue {}
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategorySimValue {}
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryExpr {}
impl ValueCategoryIsValueSimValueExprOrValueless for ValueCategoryValueless {}
pub trait ValueCategoryIsValueless: ValueCategoryIsExprOrValueless {}
impl ValueCategoryIsValueless for ValueCategoryValueless {}
pub trait ValueCategoryIsExprOrValueless: ValueCategoryIsValueSimValueExprOrValueless {}
impl ValueCategoryIsExprOrValueless for ValueCategoryExpr {}
impl ValueCategoryIsExprOrValueless for ValueCategoryValueless {}
pub trait ValueCategoryIsSimValueExprOrValueless:
ValueCategoryIsValueSimValueExprOrValueless
{
}
impl ValueCategoryIsSimValueExprOrValueless for ValueCategorySimValue {}
impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryExpr {}
impl ValueCategoryIsSimValueExprOrValueless for ValueCategoryValueless {}
trait ValueCategoryCommonSealed {}
#[expect(private_bounds)]
pub trait ValueCategoryCommon<
T: ValueCategoryCommonSealed
+ Copy
+ Ord
+ Default
+ std::fmt::Debug
+ std::hash::Hash
+ 'static
+ Send
+ Sync,
>: ValueCategory
{
type Common: ValueCategory;
}
macro_rules! impl_value_category_common {
($($T:ident),* $(,)?) => {
impl_value_category_common!([$($T,)*]);
};
([$($A:ident,)?]) => {};
([$FirstT:ident, $($T:ident,)+]) => {
impl_value_category_common!([$($T,)*]);
impl<$FirstT: ValueCategory, $($T: ValueCategory),*> ValueCategoryCommonSealed for ($FirstT, $($T),*) {}
impl<This, $FirstT, $($T),*> ValueCategoryCommon<($FirstT, $($T),*)> for This
where
$FirstT: ValueCategory,
$($T: ValueCategory,)*
This: ValueCategoryCommon<($FirstT,), Common: ValueCategoryCommon<($($T,)*)>>,
{
type Common = <<This as ValueCategoryCommon<($FirstT,)>>::Common as ValueCategoryCommon<($($T,)*)>>::Common;
}
};
}
impl_value_category_common!(T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0);
impl<T: ValueCategory> ValueCategoryCommonSealed for (T,) {}
impl ValueCategoryCommonSealed for () {}
impl<T: ValueCategory> ValueCategoryCommon<()> for T {
type Common = T;
}
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(ValueCategoryValue,)> for T {
type Common = ValueCategoryValue;
}
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(ValueCategorySimValue,)> for T {
type Common = ValueCategorySimValue;
}
impl<T: ValueCategoryIsValue> ValueCategoryCommon<(T,)> for ValueCategorySimValue {
type Common = ValueCategorySimValue;
}
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(ValueCategoryExpr,)> for T {
type Common = ValueCategoryExpr;
}
impl<T: ValueCategoryIsValueOrSimValue> ValueCategoryCommon<(T,)> for ValueCategoryExpr {
type Common = ValueCategoryExpr;
}
impl<T: ValueCategoryIsValueSimValueExprOrValueless> ValueCategoryCommon<(ValueCategoryValueless,)>
for T
{
type Common = ValueCategoryValueless;
}
impl<T: ValueCategoryIsValueSimValueOrExpr> ValueCategoryCommon<(T,)> for ValueCategoryValueless {
type Common = ValueCategoryValueless;
}
pub trait ValueCategoryDispatch: Sized {
type Type: Type;
type InputValueCategory: ValueCategory;
type Value: ValueType<ValueCategory: ValueCategoryIsValueSimValueExprOrValueless, Type = Self::Type>;
type SimValue: ValueType<ValueCategory: ValueCategoryIsSimValueExprOrValueless, Type = Self::Type>;
type Expr: ValueType<ValueCategory: ValueCategoryIsExprOrValueless, Type = Self::Type>;
type Valueless: ValueType<ValueCategory: ValueCategoryIsValueless, Type = Self::Type>;
fn dispatch_value(self) -> Self::Value
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValue>;
fn dispatch_sim_value(self) -> Self::SimValue
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategorySimValue>;
fn dispatch_expr(self) -> Self::Expr
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryExpr>;
fn dispatch_valueless(self) -> Self::Valueless
where
Self: ValueCategoryDispatch<InputValueCategory = ValueCategoryValueless>;
fn dispatch(self) -> <Self::InputValueCategory as ValueCategory>::DispatchOutput<Self> {
Self::InputValueCategory::dispatch(self)
}
fn dispatch_enum(self) -> ValueCategoryDispatchOutputEnum<Self> {
Self::InputValueCategory::dispatch_enum(self)
}
}
pub enum ValueCategoryDispatchOutputEnum<T: ValueCategoryDispatch> {
Value(T::Value),
SimValue(T::SimValue),
Expr(T::Expr),
Valueless(T::Valueless),
}
impl<T: ValueCategoryDispatch> ValueCategoryDispatchOutputEnum<T> {
pub fn try_into_value(self) -> Result<T::Value, Self> {
if let Self::Value(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_sim_value(self) -> Result<T::SimValue, Self> {
if let Self::SimValue(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_expr(self) -> Result<T::Expr, Self> {
if let Self::Expr(v) = self {
Ok(v)
} else {
Err(self)
}
}
pub fn try_into_valueless(self) -> Result<T::Valueless, Self> {
if let Self::Valueless(v) = self {
Ok(v)
} else {
Err(self)
}
}
#[track_caller]
#[cold]
fn unwrap_invalid(self, expected_kind: &'static str) -> ! {
match self {
Self::Value(_) => panic!("expected {expected_kind}, got Value"),
Self::SimValue(_) => panic!("expected {expected_kind}, got SimValue"),
Self::Expr(_) => panic!("expected {expected_kind}, got Expr"),
Self::Valueless(_) => panic!("expected {expected_kind}, got Valueless"),
}
}
#[track_caller]
pub fn into_value_unwrap(self) -> T::Value {
let Self::Value(v) = self else {
self.unwrap_invalid("Value");
};
v
}
#[track_caller]
pub fn into_sim_value_unwrap(self) -> T::SimValue {
let Self::SimValue(v) = self else {
self.unwrap_invalid("SimValue");
};
v
}
#[track_caller]
pub fn into_expr_unwrap(self) -> T::Expr {
let Self::Expr(v) = self else {
self.unwrap_invalid("Expr");
};
v
}
#[track_caller]
pub fn into_valueless_unwrap(self) -> T::Valueless {
let Self::Valueless(v) = self else {
self.unwrap_invalid("Valueless");
};
v
}
pub fn ty(&self) -> T::Type {
match self {
Self::Value(v) => v.ty(),
Self::SimValue(v) => v.ty(),
Self::Expr(v) => v.ty(),
Self::Valueless(v) => v.ty(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
type Identity<T> = T;
macro_rules! check_categories {
($($Categories:ident,)+) => {
check_categories!([] [$($Categories,)+]);
};
([$($Categories:ident,)*] []) => {};
([$($WeakerCategories:ident,)*] [$Category:ident, $($StrongerCategories:ident,)*]) => {
$(const _: $Category = Identity::<<$WeakerCategories as ValueCategoryCommon<($Category,)>>::Common> {};
const _: $Category = Identity::<<$Category as ValueCategoryCommon<($WeakerCategories,)>>::Common> {};)*
const _: $Category = Identity::<<$Category as ValueCategoryCommon<($Category,)>>::Common> {};
check_categories!([$($WeakerCategories,)* $Category,] [$($StrongerCategories,)*]);
};
}
check_categories!(
ValueCategoryValue,
ValueCategorySimValue,
ValueCategoryExpr,
ValueCategoryValueless,
);
}

View file

@ -12,7 +12,7 @@ use crate::{
clock::Clock,
enum_::{Enum, EnumType, EnumVariant},
expr::{
CastBitsTo, Expr, ExprEnum,
CastBitsTo, Expr, ExprEnum, ToExpr, ValueType,
ops::{self, VariantAccess},
target::{
Target, TargetBase, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
@ -877,7 +877,7 @@ impl<'a> Exporter<'a> {
definitions: &RcDefinitions,
const_ty: bool,
) -> Result<String> {
let from_ty = Expr::ty(value);
let from_ty = value.ty();
let mut value = self.expr(Expr::canonical(value), definitions, const_ty)?;
if from_ty.width().checked_add(1) == Some(to_ty.width())
&& !FromTy::Signed::VALUE
@ -926,7 +926,7 @@ impl<'a> Exporter<'a> {
definitions: &RcDefinitions,
const_ty: bool,
) -> Result<String> {
let base_width = Expr::ty(base).width();
let base_width = base.ty().width();
let base = self.expr(Expr::canonical(base), definitions, const_ty)?;
if range.is_empty() {
Ok(format!("tail({base}, {base_width})"))
@ -1470,7 +1470,7 @@ impl<'a> Exporter<'a> {
ExprEnum::SIntLiteral(literal) => Ok(self.sint_literal(&literal)),
ExprEnum::BoolLiteral(literal) => Ok(self.bool_literal(literal)),
ExprEnum::PhantomConst(ty) => self.expr(
UInt[0].zero().cast_bits_to(ty.canonical()),
UInt[0].zero().to_expr().cast_bits_to(ty.canonical()),
definitions,
const_ty,
),
@ -1653,7 +1653,7 @@ impl<'a> Exporter<'a> {
let value_str = self.expr(expr.arg(), definitions, const_ty)?;
self.expr_cast_to_bits(
value_str,
Expr::ty(expr.arg()),
expr.arg().ty(),
definitions,
Indent {
indent_depth: &Cell::new(0),
@ -1776,7 +1776,7 @@ impl<'a> Exporter<'a> {
let mut out = self.expr(Expr::canonical(expr.base()), definitions, const_ty)?;
let name = self
.type_state
.get_bundle_field(Expr::ty(expr.base()), expr.field_name())?;
.get_bundle_field(expr.base().ty(), expr.field_name())?;
write!(out, ".{name}").unwrap();
Ok(out)
}
@ -2101,12 +2101,12 @@ impl<'a> Exporter<'a> {
rhs,
source_location,
}) => {
if Expr::ty(lhs) != Expr::ty(rhs) {
if lhs.ty() != rhs.ty() {
writeln!(
body,
"{indent}; connect different types:\n{indent}; lhs: {:?}\n{indent}; rhs: {:?}",
Expr::ty(lhs),
Expr::ty(rhs),
lhs.ty(),
rhs.ty(),
)
.unwrap();
}
@ -2200,7 +2200,7 @@ impl<'a> Exporter<'a> {
FileInfo::new(source_location),
)
.unwrap();
let enum_ty = Expr::ty(expr);
let enum_ty = expr.ty();
let match_arms_indent = indent.push();
for ((variant_index, variant), match_arm_block) in
enum_ty.variants().iter().enumerate().zip(blocks)

View file

@ -4,16 +4,19 @@
use crate::{
array::ArrayType,
expr::{
Expr, NotALiteralExpr, ToExpr, ToLiteralBits,
Expr, NotALiteralExpr, ToExpr, ToLiteralBits, ToSimValueInner, ToValueless, ValueType,
Valueless,
target::{GetTarget, Target},
value_category::ValueCategoryValue,
},
hdl,
intern::{Intern, Interned, Memoize},
sim::value::{SimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
CanonicalType, FillInDefaultedGenerics, OpaqueSimValueSize, OpaqueSimValueSlice,
OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties,
impl_match_variant_as_self,
},
util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range},
};
@ -31,7 +34,7 @@ use std::{
num::NonZero,
ops::{Index, Not, Range, RangeBounds, RangeInclusive},
str::FromStr,
sync::Arc,
sync::{Arc, OnceLock},
};
mod uint_in_range;
@ -60,8 +63,62 @@ mod sealed {
pub const DYN_SIZE: usize = !0;
pub type DynSize = ConstUsize<DYN_SIZE>;
trait KnownSizeBaseSealed {}
impl<const N: usize> KnownSizeBaseSealed for [(); N] {}
#[expect(private_bounds)]
pub trait KnownSizeBase: KnownSizeBaseSealed {}
macro_rules! impl_known_size_base {
($($size:literal),* $(,)?) => {
$(impl KnownSizeBase for [(); $size] {})*
};
}
#[rustfmt::skip]
impl_known_size_base! {
0x000, 0x001, 0x002, 0x003, 0x004, 0x005, 0x006, 0x007, 0x008, 0x009, 0x00A, 0x00B, 0x00C, 0x00D, 0x00E, 0x00F,
0x010, 0x011, 0x012, 0x013, 0x014, 0x015, 0x016, 0x017, 0x018, 0x019, 0x01A, 0x01B, 0x01C, 0x01D, 0x01E, 0x01F,
0x020, 0x021, 0x022, 0x023, 0x024, 0x025, 0x026, 0x027, 0x028, 0x029, 0x02A, 0x02B, 0x02C, 0x02D, 0x02E, 0x02F,
0x030, 0x031, 0x032, 0x033, 0x034, 0x035, 0x036, 0x037, 0x038, 0x039, 0x03A, 0x03B, 0x03C, 0x03D, 0x03E, 0x03F,
0x040, 0x041, 0x042, 0x043, 0x044, 0x045, 0x046, 0x047, 0x048, 0x049, 0x04A, 0x04B, 0x04C, 0x04D, 0x04E, 0x04F,
0x050, 0x051, 0x052, 0x053, 0x054, 0x055, 0x056, 0x057, 0x058, 0x059, 0x05A, 0x05B, 0x05C, 0x05D, 0x05E, 0x05F,
0x060, 0x061, 0x062, 0x063, 0x064, 0x065, 0x066, 0x067, 0x068, 0x069, 0x06A, 0x06B, 0x06C, 0x06D, 0x06E, 0x06F,
0x070, 0x071, 0x072, 0x073, 0x074, 0x075, 0x076, 0x077, 0x078, 0x079, 0x07A, 0x07B, 0x07C, 0x07D, 0x07E, 0x07F,
0x080, 0x081, 0x082, 0x083, 0x084, 0x085, 0x086, 0x087, 0x088, 0x089, 0x08A, 0x08B, 0x08C, 0x08D, 0x08E, 0x08F,
0x090, 0x091, 0x092, 0x093, 0x094, 0x095, 0x096, 0x097, 0x098, 0x099, 0x09A, 0x09B, 0x09C, 0x09D, 0x09E, 0x09F,
0x0A0, 0x0A1, 0x0A2, 0x0A3, 0x0A4, 0x0A5, 0x0A6, 0x0A7, 0x0A8, 0x0A9, 0x0AA, 0x0AB, 0x0AC, 0x0AD, 0x0AE, 0x0AF,
0x0B0, 0x0B1, 0x0B2, 0x0B3, 0x0B4, 0x0B5, 0x0B6, 0x0B7, 0x0B8, 0x0B9, 0x0BA, 0x0BB, 0x0BC, 0x0BD, 0x0BE, 0x0BF,
0x0C0, 0x0C1, 0x0C2, 0x0C3, 0x0C4, 0x0C5, 0x0C6, 0x0C7, 0x0C8, 0x0C9, 0x0CA, 0x0CB, 0x0CC, 0x0CD, 0x0CE, 0x0CF,
0x0D0, 0x0D1, 0x0D2, 0x0D3, 0x0D4, 0x0D5, 0x0D6, 0x0D7, 0x0D8, 0x0D9, 0x0DA, 0x0DB, 0x0DC, 0x0DD, 0x0DE, 0x0DF,
0x0E0, 0x0E1, 0x0E2, 0x0E3, 0x0E4, 0x0E5, 0x0E6, 0x0E7, 0x0E8, 0x0E9, 0x0EA, 0x0EB, 0x0EC, 0x0ED, 0x0EE, 0x0EF,
0x0F0, 0x0F1, 0x0F2, 0x0F3, 0x0F4, 0x0F5, 0x0F6, 0x0F7, 0x0F8, 0x0F9, 0x0FA, 0x0FB, 0x0FC, 0x0FD, 0x0FE, 0x0FF,
0x100, 0x101, 0x102, 0x103, 0x104, 0x105, 0x106, 0x107, 0x108, 0x109, 0x10A, 0x10B, 0x10C, 0x10D, 0x10E, 0x10F,
0x110, 0x111, 0x112, 0x113, 0x114, 0x115, 0x116, 0x117, 0x118, 0x119, 0x11A, 0x11B, 0x11C, 0x11D, 0x11E, 0x11F,
0x120, 0x121, 0x122, 0x123, 0x124, 0x125, 0x126, 0x127, 0x128, 0x129, 0x12A, 0x12B, 0x12C, 0x12D, 0x12E, 0x12F,
0x130, 0x131, 0x132, 0x133, 0x134, 0x135, 0x136, 0x137, 0x138, 0x139, 0x13A, 0x13B, 0x13C, 0x13D, 0x13E, 0x13F,
0x140, 0x141, 0x142, 0x143, 0x144, 0x145, 0x146, 0x147, 0x148, 0x149, 0x14A, 0x14B, 0x14C, 0x14D, 0x14E, 0x14F,
0x150, 0x151, 0x152, 0x153, 0x154, 0x155, 0x156, 0x157, 0x158, 0x159, 0x15A, 0x15B, 0x15C, 0x15D, 0x15E, 0x15F,
0x160, 0x161, 0x162, 0x163, 0x164, 0x165, 0x166, 0x167, 0x168, 0x169, 0x16A, 0x16B, 0x16C, 0x16D, 0x16E, 0x16F,
0x170, 0x171, 0x172, 0x173, 0x174, 0x175, 0x176, 0x177, 0x178, 0x179, 0x17A, 0x17B, 0x17C, 0x17D, 0x17E, 0x17F,
0x180, 0x181, 0x182, 0x183, 0x184, 0x185, 0x186, 0x187, 0x188, 0x189, 0x18A, 0x18B, 0x18C, 0x18D, 0x18E, 0x18F,
0x190, 0x191, 0x192, 0x193, 0x194, 0x195, 0x196, 0x197, 0x198, 0x199, 0x19A, 0x19B, 0x19C, 0x19D, 0x19E, 0x19F,
0x1A0, 0x1A1, 0x1A2, 0x1A3, 0x1A4, 0x1A5, 0x1A6, 0x1A7, 0x1A8, 0x1A9, 0x1AA, 0x1AB, 0x1AC, 0x1AD, 0x1AE, 0x1AF,
0x1B0, 0x1B1, 0x1B2, 0x1B3, 0x1B4, 0x1B5, 0x1B6, 0x1B7, 0x1B8, 0x1B9, 0x1BA, 0x1BB, 0x1BC, 0x1BD, 0x1BE, 0x1BF,
0x1C0, 0x1C1, 0x1C2, 0x1C3, 0x1C4, 0x1C5, 0x1C6, 0x1C7, 0x1C8, 0x1C9, 0x1CA, 0x1CB, 0x1CC, 0x1CD, 0x1CE, 0x1CF,
0x1D0, 0x1D1, 0x1D2, 0x1D3, 0x1D4, 0x1D5, 0x1D6, 0x1D7, 0x1D8, 0x1D9, 0x1DA, 0x1DB, 0x1DC, 0x1DD, 0x1DE, 0x1DF,
0x1E0, 0x1E1, 0x1E2, 0x1E3, 0x1E4, 0x1E5, 0x1E6, 0x1E7, 0x1E8, 0x1E9, 0x1EA, 0x1EB, 0x1EC, 0x1ED, 0x1EE, 0x1EF,
0x1F0, 0x1F1, 0x1F2, 0x1F3, 0x1F4, 0x1F5, 0x1F6, 0x1F7, 0x1F8, 0x1F9, 0x1FA, 0x1FB, 0x1FC, 0x1FD, 0x1FE, 0x1FF,
0x200,
}
pub trait KnownSize:
GenericConstUsize + sealed::SizeTypeSealed + sealed::SizeSealed + Default
GenericConstUsize
+ sealed::SizeTypeSealed
+ sealed::SizeSealed
+ Default
+ FillInDefaultedGenerics<Type = Self>
{
const SIZE: Self;
type ArrayMatch<Element: Type>: AsRef<[Expr<Element>]>
@ -89,35 +146,15 @@ pub trait KnownSize:
+ ToSimValueWithType<ArrayType<Element, Self>>;
}
macro_rules! known_widths {
([] $($bits:literal)+) => {
impl KnownSize for ConstUsize<{
let v = 0;
$(let v = v * 2 + $bits;)*
v
}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
([2 $($rest:tt)*] $($bits:literal)+) => {
known_widths!([$($rest)*] $($bits)* 0);
known_widths!([$($rest)*] $($bits)* 1);
};
([2 $($rest:tt)*]) => {
known_widths!([$($rest)*] 0);
known_widths!([$($rest)*] 1);
impl KnownSize for ConstUsize<{2 $(* $rest)*}> {
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; Self::VALUE];
type ArraySimValue<Element: Type> = [SimValue<Element>; Self::VALUE];
}
};
impl<const N: usize> KnownSize for ConstUsize<N>
where
[(); N]: KnownSizeBase,
{
const SIZE: Self = Self;
type ArrayMatch<Element: Type> = [Expr<Element>; N];
type ArraySimValue<Element: Type> = [SimValue<Element>; N];
}
known_widths!([2 2 2 2 2 2 2 2 2]);
pub trait SizeType:
sealed::SizeTypeSealed
+ Copy
@ -129,6 +166,7 @@ pub trait SizeType:
+ 'static
+ Serialize
+ DeserializeOwned
+ FillInDefaultedGenerics<Type = Self>
{
type Size: Size<SizeType = Self>;
}
@ -530,6 +568,23 @@ fn deserialize_int_value<'de, D: Deserializer<'de>>(
})
}
macro_rules! impl_valueless_op_forward {
(
#[forward_to = $ForwardTrait:path]
impl<$($G:ident: $Bound:path),*> $TraitPath:ident$(::$TraitPathRest:ident)+<$($Rhs:ty)?> for $SelfTy:ty {
$($(type $Output:ident;)?
use $forward_fn:path as $fn:ident($self:ident $(, $rhs:ident)?);)?
}
) => {
impl<$($G: $Bound),*> $TraitPath$(::$TraitPathRest)+<$($Rhs)?> for $SelfTy {
$($(type $Output = <$SelfTy as $ForwardTrait>::$Output;)?
fn $fn($self $(, $rhs: $Rhs)?) $(-> Self::$Output)? {
$forward_fn($self $(, $rhs)?)
})?
}
};
}
macro_rules! impl_int {
($pretty_name:ident, $name:ident, $generic_name:ident, $value:ident, $SIGNED:literal) => {
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
@ -582,6 +637,176 @@ macro_rules! impl_int {
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Add<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<$name>;
fn add(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
Valueless::new($name::new_dyn(
self.ty()
.width()
.max(rhs.ty().width())
.checked_add(1)
.expect("width too big"),
))
}
}
impl_valueless_op_forward! {
#[forward_to = std::ops::Add<Valueless<$name<RhsWidth>>>]
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Sub<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output;
use std::ops::Add::<Valueless<$name<RhsWidth>>>::add as sub(self, rhs);
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitOr<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<UInt>;
fn bitor(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
Valueless::new(UInt::new_dyn(self.ty().width().max(rhs.ty().width())))
}
}
impl_valueless_op_forward! {
#[forward_to = std::ops::BitOr<Valueless<$name<RhsWidth>>>]
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitAnd<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output;
use std::ops::BitOr::<Valueless<$name<RhsWidth>>>::bitor as bitand(self, rhs);
}
}
impl_valueless_op_forward! {
#[forward_to = std::ops::BitOr<Valueless<$name<RhsWidth>>>]
impl<LhsWidth: Size, RhsWidth: Size> std::ops::BitXor<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output;
use std::ops::BitOr::<Valueless<$name<RhsWidth>>>::bitor as bitxor(self, rhs);
}
}
impl<Width: Size> std::ops::Not for Valueless<$name<Width>> {
type Output = Valueless<UIntType<Width>>;
fn not(self) -> Self::Output {
Valueless::new(self.ty().as_same_width_uint())
}
}
impl<Width: Size> std::ops::Not for $value<Width> {
type Output = UIntValue<Width>;
fn not(mut self) -> Self::Output {
// modifies in-place despite using `Not::not`
let _ = Not::not(self.bits_mut());
UIntValue::new(self.into_bits())
}
}
impl<Width: Size> std::ops::Not for &'_ $value<Width> {
type Output = UIntValue<Width>;
fn not(self) -> Self::Output {
$value::not(self.clone())
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Mul<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<$name>;
fn mul(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
Valueless::new($name::new_dyn(
self.ty()
.width()
.checked_add(rhs.ty().width())
.expect("width too big"),
))
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Rem<Valueless<$name<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<$name>;
fn rem(self, rhs: Valueless<$name<RhsWidth>>) -> Self::Output {
Valueless::new($name::new_dyn(self.ty().width().min(rhs.ty().width())))
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Shl<Valueless<UIntType<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<$name>;
#[track_caller]
fn shl(self, rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
let Some(pow2_rhs_width) = rhs
.ty()
.width()
.try_into()
.ok()
.and_then(|v| 2usize.checked_pow(v))
else {
panic!(
"dynamic left-shift amount's bit-width is too big, try casting the shift \
amount to a smaller bit-width before shifting"
);
};
Valueless::new($name::new_dyn(
(pow2_rhs_width - 1)
.checked_add(self.ty().width())
.expect("width too big"),
))
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Shr<Valueless<UIntType<RhsWidth>>>
for Valueless<$name<LhsWidth>>
{
type Output = Valueless<$name<LhsWidth>>;
fn shr(self, _rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
self
}
}
impl<LhsWidth: Size> std::ops::Shl<usize> for Valueless<$name<LhsWidth>> {
type Output = Valueless<$name>;
fn shl(self, rhs: usize) -> Self::Output {
Valueless::new($name::new_dyn(
self.ty().width().checked_add(rhs).expect("width too big"),
))
}
}
impl<LhsWidth: Size> std::ops::Shr<usize> for Valueless<$name<LhsWidth>> {
type Output = Valueless<$name>;
fn shr(self, rhs: usize) -> Self::Output {
let width = self.ty().width().saturating_sub(rhs);
Valueless::new($name::new_dyn(if $SIGNED && width == 0 {
1
} else {
width
}))
}
}
impl<Width: Size> sealed::BoolOrIntTypeSealed for $name<Width> {}
impl<Width: Size> BoolOrIntType for $name<Width> {
@ -843,6 +1068,12 @@ macro_rules! impl_int {
}
}
impl<'a, Width: Size> From<&'a $value<Width>> for BigInt {
fn from(v: &'a $value<Width>) -> BigInt {
v.to_bigint()
}
}
impl<Width: Size> $value<Width> {
pub fn width(&self) -> usize {
if let Some(retval) = Width::KNOWN_VALUE {
@ -861,11 +1092,6 @@ macro_rules! impl_int {
pub fn into_bits(self) -> Arc<BitVec> {
self.bits
}
pub fn ty(&self) -> $name<Width> {
$name {
width: Width::from_usize(self.width()),
}
}
pub fn as_dyn_int(self) -> $value {
$value {
bits: self.bits,
@ -878,6 +1104,38 @@ macro_rules! impl_int {
pub fn bits_mut(&mut self) -> &mut BitSlice {
Arc::<BitVec>::make_mut(&mut self.bits)
}
pub fn hdl_cmp<R: Size>(&self, other: &$value<R>) -> std::cmp::Ordering {
self.to_bigint().cmp(&other.to_bigint())
}
}
impl<Width: Size> ValueType for $value<Width> {
type Type = $name<Width>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
$name {
width: Width::from_usize(self.width()),
}
}
}
impl<'a, Width: Size> ToSimValueInner<'a> for $value<Width> {
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
Cow::Borrowed(this)
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
Cow::Owned(this)
}
}
impl<'a, Width: Size> ToSimValueInner<'a> for &'a $value<Width> {
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
Cow::Borrowed(&**this)
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
Cow::Borrowed(this)
}
}
impl<Width: Size> ToLiteralBits for $value<Width> {
@ -1015,11 +1273,58 @@ impl SInt {
}
}
macro_rules! impl_prim_int {
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Div<Valueless<UIntType<RhsWidth>>>
for Valueless<UIntType<LhsWidth>>
{
type Output = Self;
fn div(self, _rhs: Valueless<UIntType<RhsWidth>>) -> Self::Output {
self
}
}
impl<LhsWidth: Size, RhsWidth: Size> std::ops::Div<Valueless<SIntType<RhsWidth>>>
for Valueless<SIntType<LhsWidth>>
{
type Output = Valueless<SInt>;
fn div(self, _rhs: Valueless<SIntType<RhsWidth>>) -> Self::Output {
Valueless::new(SInt::new_dyn(
self.ty().width().checked_add(1).expect("width too big"),
))
}
}
impl<Width: Size> std::ops::Neg for Valueless<SIntType<Width>> {
type Output = Valueless<SInt>;
fn neg(self) -> Self::Output {
Valueless::new(SInt::new_dyn(
self.ty().width().checked_add(1).expect("width too big"),
))
}
}
impl<Width: Size> std::ops::Neg for SIntValue<Width> {
type Output = SIntValue;
fn neg(self) -> Self::Output {
-&self
}
}
impl<Width: Size> std::ops::Neg for &'_ SIntValue<Width> {
type Output = SIntValue;
fn neg(self) -> Self::Output {
let ty = self.to_valueless().neg().ty();
ty.from_bigint_wrapping(&-self.to_bigint())
}
}
macro_rules! impl_prim_int_to_expr {
(
$(#[$meta:meta])*
$prim_int:ident, $ty:ty
) => {
$(#[$meta])*
impl From<$prim_int> for <$ty as BoolOrIntType>::Value {
fn from(v: $prim_int) -> Self {
<$ty>::le_bytes_to_value_wrapping(
@ -1028,15 +1333,32 @@ macro_rules! impl_prim_int {
)
}
}
$(#[$meta])*
impl From<NonZero<$prim_int>> for <$ty as BoolOrIntType>::Value {
fn from(v: NonZero<$prim_int>) -> Self {
v.get().into()
}
}
$(#[$meta])*
impl ToExpr for $prim_int {
impl<'a> ToSimValueInner<'a> for $prim_int {
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
Self::into_sim_value_inner(*this)
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
Cow::Owned(this.into())
}
}
$(#[$meta])*
impl ValueType for $prim_int {
type Type = $ty;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
StaticType::TYPE
}
}
$(#[$meta])*
impl ToExpr for $prim_int {
fn to_expr(&self) -> Expr<Self::Type> {
<$ty>::le_bytes_to_expr_wrapping(
&self.to_le_bytes(),
@ -1045,9 +1367,25 @@ macro_rules! impl_prim_int {
}
}
$(#[$meta])*
impl ToExpr for NonZero<$prim_int> {
impl<'a> ToSimValueInner<'a> for NonZero<$prim_int> {
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
Self::into_sim_value_inner(*this)
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
Cow::Owned(this.into())
}
}
$(#[$meta])*
impl ValueType for NonZero<$prim_int> {
type Type = $ty;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
StaticType::TYPE
}
}
$(#[$meta])*
impl ToExpr for NonZero<$prim_int> {
fn to_expr(&self) -> Expr<Self::Type> {
self.get().to_expr()
}
@ -1055,28 +1393,208 @@ macro_rules! impl_prim_int {
};
}
impl_prim_int!(u8, UInt<8>);
impl_prim_int!(u16, UInt<16>);
impl_prim_int!(u32, UInt<32>);
impl_prim_int!(u64, UInt<64>);
impl_prim_int!(u128, UInt<128>);
impl_prim_int!(i8, SInt<8>);
impl_prim_int!(i16, SInt<16>);
impl_prim_int!(i32, SInt<32>);
impl_prim_int!(i64, SInt<64>);
impl_prim_int!(i128, SInt<128>);
macro_rules! impl_for_all_prim_uints {
(
#[size =, $(rest:tt)*]
$m:ident!()
) => {
impl_for_all_prim_uints!(
#[usize =, $($rest)*]
$m!()
);
};
(
#[size = size, $(rest:tt)*]
$m:ident!()
) => {
impl_for_all_prim_uints!(
#[usize = usize, $($rest)*]
$m!()
);
};
(
#[usize = $($usize:ident)?, NonZero = $NonZero:ident]
$m:ident!()
) => {
impl_for_all_prim_uints!(
#[usize = $($usize)?, NonZero = $NonZero, NonZero<usize> = $((NonZero<$usize>))?]
$m!()
);
};
(
#[usize = $($usize:ident)?, NonZero =]
$m:ident!()
) => {
impl_for_all_prim_uints!(
#[usize = $($usize)?, NonZero =, NonZero<usize> =]
$m!()
);
};
(
#[usize = $($usize:ident)?, NonZero = $($NonZero:ident)?, NonZero<usize> = $(($($NonZeroUsize:tt)*))?]
$m:ident!()
) => {
$m!(u8, UInt<8>);
$m!(u16, UInt<16>);
$m!(u32, UInt<32>);
$m!(u64, UInt<64>);
$m!(u128, UInt<128>);
$($m!($NonZero<u8>, UInt<8>);)?
$($m!($NonZero<u16>, UInt<16>);)?
$($m!($NonZero<u32>, UInt<32>);)?
$($m!($NonZero<u64>, UInt<64>);)?
$($m!($NonZero<u128>, UInt<128>);)?
$($m!(
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
$usize, UInt<64>
);)?
$($m!(
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
$($NonZeroUsize)*, UInt<64>
);)?
};
}
impl_prim_int!(
/// for portability reasons, [`usize`] always translates to [`UInt<64>`][type@UInt]
usize, UInt<64>
macro_rules! impl_for_all_prim_sints {
(
#[size =, $(rest:tt)*]
$m:ident!()
) => {
impl_for_all_prim_sints!(
#[isize =, $($rest)*]
$m!()
);
};
(
#[size = size, $(rest:tt)*]
$m:ident!()
) => {
impl_for_all_prim_sints!(
#[isize = isize, $($rest)*]
$m!()
);
};
(
#[isize = $($isize:ident)?, NonZero = $NonZero:ident]
$m:ident!()
) => {
impl_for_all_prim_sints!(
#[isize = $($isize)?, NonZero = $NonZero, NonZero<isize> = $((NonZero<$isize>))?]
$m!()
);
};
(
#[isize = $($isize:ident)?, NonZero =]
$m:ident!()
) => {
impl_for_all_prim_sints!(
#[isize = $($isize)?, NonZero =, NonZero<isize> =]
$m!()
);
};
(
#[isize = $($isize:ident)?, NonZero = $($NonZero:ident)?, NonZero<isize> = $(($($NonZeroIsize:tt)*))?]
$m:ident!()
) => {
$m!(i8, SInt<8>);
$m!(i16, SInt<16>);
$m!(i32, SInt<32>);
$m!(i64, SInt<64>);
$m!(i128, SInt<128>);
$($m!($NonZero<i8>, SInt<8>);)?
$($m!($NonZero<i16>, SInt<16>);)?
$($m!($NonZero<i32>, SInt<32>);)?
$($m!($NonZero<i64>, SInt<64>);)?
$($m!($NonZero<i128>, SInt<128>);)?
$($m!(
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
$isize, SInt<64>
);)?
$($m!(
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
$($NonZeroIsize)*, SInt<64>
);)?
};
}
impl_for_all_prim_uints!(
#[usize = usize, NonZero =]
impl_prim_int_to_expr!()
);
impl_prim_int!(
/// for portability reasons, [`isize`] always translates to [`SInt<64>`][type@SInt]
isize, SInt<64>
impl_for_all_prim_sints!(
#[isize = isize, NonZero =]
impl_prim_int_to_expr!()
);
pub trait BoolOrIntType: Type + sealed::BoolOrIntTypeSealed {
macro_rules! impl_prim_int_from_value {
($prim_int:ident, UInt<$width:literal>) => {
impl_prim_int_from_value!($prim_int, UInt<$width>, UIntValue<ConstUsize<$width>>);
};
($prim_int:ident, SInt<$width:literal>) => {
impl_prim_int_from_value!($prim_int, SInt<$width>, SIntValue<ConstUsize<$width>>);
};
($prim_int:ident, $ty:ty, $value:ty) => {
impl From<$value> for $prim_int {
fn from(value: $value) -> Self {
value.as_int()
}
}
impl<'a> From<&'a mut $value> for $prim_int {
fn from(value: &'a mut $value) -> Self {
value.as_int()
}
}
impl From<SimValue<$ty>> for $prim_int {
fn from(value: SimValue<$ty>) -> Self {
value.as_int()
}
}
impl<'a> From<&'a mut SimValue<$ty>> for $prim_int {
fn from(value: &'a mut SimValue<$ty>) -> Self {
value.as_int()
}
}
impl<'a> From<&'a SimValue<$ty>> for $prim_int {
fn from(value: &'a SimValue<$ty>) -> Self {
value.as_int()
}
}
impl<'a> From<&'a $value> for $prim_int {
fn from(value: &'a $value) -> Self {
value.as_int()
}
}
impl $value {
pub fn as_int(&self) -> $prim_int {
let mut le_bytes = (0 as $prim_int).to_le_bytes();
let bitslice = BitSlice::<u8, Lsb0>::from_slice_mut(&mut le_bytes);
bitslice.clone_from_bitslice(self.bits());
$prim_int::from_le_bytes(le_bytes)
}
}
};
}
impl_for_all_prim_uints!(
#[usize =, NonZero =]
impl_prim_int_from_value!()
);
impl_for_all_prim_sints!(
#[isize =, NonZero =]
impl_prim_int_from_value!()
);
pub trait BoolOrIntType:
Type<SimValue = <Self as BoolOrIntType>::Value> + sealed::BoolOrIntTypeSealed
{
type Width: Size;
type Signed: GenericConstBool;
type Value: Clone
@ -1301,12 +1819,80 @@ impl StaticType for Bool {
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
}
impl From<bool> for UIntValue<ConstUsize<1>> {
fn from(value: bool) -> Self {
static VALUES: OnceLock<[UIntValue<ConstUsize<1>>; 2]> = OnceLock::new();
let values = VALUES
.get_or_init(|| std::array::from_fn(|i| UInt::<1>::new_static().from_int_wrapping(i)));
values[value as usize].clone()
}
}
impl From<bool> for SIntValue<ConstUsize<1>> {
fn from(value: bool) -> Self {
SIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
}
}
impl From<bool> for UIntValue {
fn from(value: bool) -> Self {
UIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
}
}
impl From<bool> for SIntValue {
fn from(value: bool) -> Self {
SIntValue::new(UIntValue::<ConstUsize<1>>::from(value).into_bits())
}
}
impl ToLiteralBits for bool {
fn to_literal_bits(&self) -> Result<Interned<BitSlice>, NotALiteralExpr> {
Ok(interned_bit(*self))
}
}
impl<'a> ToSimValueInner<'a> for bool {
fn to_sim_value_inner(this: &Self) -> Cow<'_, <Self::Type as Type>::SimValue> {
Cow::Owned(*this)
}
fn into_sim_value_inner(this: Self) -> Cow<'a, <Self::Type as Type>::SimValue> {
Cow::Owned(this)
}
}
impl std::ops::Not for Valueless<Bool> {
type Output = Valueless<Bool>;
fn not(self) -> Self::Output {
self
}
}
impl std::ops::BitAnd for Valueless<Bool> {
type Output = Valueless<Bool>;
fn bitand(self, _rhs: Valueless<Bool>) -> Self::Output {
self
}
}
impl std::ops::BitOr for Valueless<Bool> {
type Output = Valueless<Bool>;
fn bitor(self, _rhs: Valueless<Bool>) -> Self::Output {
self
}
}
impl std::ops::BitXor for Valueless<Bool> {
type Output = Valueless<Bool>;
fn bitxor(self, _rhs: Valueless<Bool>) -> Self::Output {
self
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -4,13 +4,13 @@
use crate::{
bundle::{Bundle, BundleField, BundleType, BundleTypePropertiesBuilder, NoBuilder},
expr::{
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd,
ops::{ExprCastTo, ExprPartialEq, ExprPartialOrd},
CastBitsTo, CastTo, CastToBits, CastToImpl, Expr, HdlPartialEq, HdlPartialEqImpl,
HdlPartialOrd, HdlPartialOrdImpl,
},
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType},
int::{Bool, DynSize, KnownSize, Size, SizeType, UInt, UIntType, UIntValue},
intern::{Intern, InternSlice, Interned},
phantom_const::PhantomConst,
sim::value::{SimValue, SimValuePartialEq, ToSimValueWithType},
sim::value::{SimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
@ -22,7 +22,7 @@ use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{Error, Visitor, value::UsizeDeserializer},
};
use std::{fmt, marker::PhantomData, ops::Index};
use std::{borrow::Cow, fmt, marker::PhantomData, ops::Index};
const UINT_IN_RANGE_TYPE_FIELD_NAMES: [&'static str; 2] = ["value", "range"];
@ -135,30 +135,57 @@ impl ToSimValueWithType<UIntInRangeMaskType> for bool {
}
}
impl ExprCastTo<Bool> for UIntInRangeMaskType {
fn cast_to(src: Expr<Self>, to_type: Bool) -> Expr<Bool> {
src.cast_to_bits().cast_to(to_type)
impl CastToImpl<Bool> for UIntInRangeMaskType {
type ValueOutput = bool;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
_to_type: Bool,
) -> Self::ValueOutput {
*value
}
fn cast_expr_to(value: Expr<Self>, to_type: Bool) -> Expr<Bool> {
value.cast_to_bits().cast_to(to_type)
}
}
impl ExprCastTo<UIntInRangeMaskType> for Bool {
fn cast_to(src: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
src.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
impl CastToImpl<UIntInRangeMaskType> for Bool {
type ValueOutput = SimValue<UIntInRangeMaskType>;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
to_type: UIntInRangeMaskType,
) -> Self::ValueOutput {
SimValue::from_value(to_type, *value)
}
fn cast_expr_to(value: Expr<Self>, to_type: UIntInRangeMaskType) -> Expr<UIntInRangeMaskType> {
value.cast_to_static::<UInt<1>>().cast_bits_to(to_type)
}
}
impl ExprPartialEq<Self> for UIntInRangeMaskType {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
impl HdlPartialEqImpl<Self> for UIntInRangeMaskType {
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: Self,
rhs_value: Cow<'_, Self::SimValue>,
) -> bool {
*lhs_value == *rhs_value
}
#[track_caller]
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
}
}
impl SimValuePartialEq<Self> for UIntInRangeMaskType {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
#[track_caller]
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs.cast_to_bits())
}
}
@ -482,36 +509,64 @@ macro_rules! define_uint_in_range_type {
}
}
impl<Start: Size, End: Size, Width: Size> ExprCastTo<UIntType<Width>>
impl<Start: Size, End: Size, Width: Size> CastToImpl<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
fn cast_to(src: Expr<Self>, to_type: UIntType<Width>) -> Expr<UIntType<Width>> {
src.cast_to_bits().cast_to(to_type)
type ValueOutput = UIntValue<Width>;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
to_type: UIntType<Width>,
) -> Self::ValueOutput {
value.cast_to(to_type)
}
fn cast_expr_to(value: Expr<Self>, to_type: UIntType<Width>) -> Expr<UIntType<Width>> {
value.cast_to_bits().cast_to(to_type)
}
}
impl<Start: Size, End: Size, Width: Size> ExprCastTo<$UIntInRangeType<Start, End>>
impl<Start: Size, End: Size, Width: Size> CastToImpl<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
fn cast_to(
src: Expr<Self>,
type ValueOutput = SimValue<$UIntInRangeType<Start, End>>;
fn cast_value_to(
_this: Self,
value: Cow<'_, Self::SimValue>,
to_type: $UIntInRangeType<Start, End>,
) -> Self::ValueOutput {
value.cast_to(to_type.value).cast_bits_to(to_type)
}
fn cast_expr_to(
value: Expr<Self>,
to_type: $UIntInRangeType<Start, End>,
) -> Expr<$UIntInRangeType<Start, End>> {
src.cast_to(to_type.value).cast_bits_to(to_type)
value.cast_to(to_type.value).cast_bits_to(to_type)
}
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
ExprPartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
HdlPartialEqImpl<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn cmp_eq(
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
*lhs_value == *rhs_value
}
fn cmp_expr_eq(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(
fn cmp_expr_ne(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
@ -520,28 +575,60 @@ macro_rules! define_uint_in_range_type {
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
ExprPartialOrd<$UIntInRangeType<RhsStart, RhsEnd>>
HdlPartialOrdImpl<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn cmp_lt(
fn cmp_value_lt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::lt(&*lhs_value, &*rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::le(&*lhs_value, &*rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::gt(&*lhs_value, &*rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<RhsStart, RhsEnd>,
rhs_value: Cow<'_, <$UIntInRangeType<RhsStart, RhsEnd> as Type>::SimValue>,
) -> bool {
PartialOrd::ge(&*lhs_value, &*rhs_value)
}
fn cmp_expr_lt(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs.cast_to_bits())
}
fn cmp_le(
fn cmp_expr_le(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs.cast_to_bits())
}
fn cmp_gt(
fn cmp_expr_gt(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs.cast_to_bits())
}
fn cmp_ge(
fn cmp_expr_ge(
lhs: Expr<Self>,
rhs: Expr<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> Expr<Bool> {
@ -549,70 +636,138 @@ macro_rules! define_uint_in_range_type {
}
}
impl<LhsStart: Size, LhsEnd: Size, RhsStart: Size, RhsEnd: Size>
SimValuePartialEq<$UIntInRangeType<RhsStart, RhsEnd>>
for $UIntInRangeType<LhsStart, LhsEnd>
{
fn sim_value_eq(
this: &SimValue<Self>,
other: &SimValue<$UIntInRangeType<RhsStart, RhsEnd>>,
) -> bool {
**this == **other
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<UIntType<Width>>
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialEq::cmp_eq(&*lhs_value, &*rhs_value)
}
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_eq(rhs)
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ne(rhs)
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialEq<$UIntInRangeType<Start, End>>
impl<Start: Size, End: Size, Width: Size> HdlPartialEqImpl<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialEq::cmp_eq(&*lhs_value, *rhs_value)
}
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_eq(rhs.cast_to_bits())
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ne(rhs.cast_to_bits())
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<UIntType<Width>>
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<UIntType<Width>>
for $UIntInRangeType<Start, End>
{
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_value_lt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_lt(&*lhs_value, &*rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_le(&*lhs_value, &*rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_gt(&*lhs_value, &*rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: UIntType<Width>,
rhs_value: Cow<'_, UIntValue<Width>>,
) -> bool {
HdlPartialOrd::cmp_ge(&*lhs_value, &*rhs_value)
}
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_lt(rhs)
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_le(rhs)
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_gt(rhs)
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<UIntType<Width>>) -> Expr<Bool> {
lhs.cast_to_bits().cmp_ge(rhs)
}
}
impl<Start: Size, End: Size, Width: Size> ExprPartialOrd<$UIntInRangeType<Start, End>>
impl<Start: Size, End: Size, Width: Size> HdlPartialOrdImpl<$UIntInRangeType<Start, End>>
for UIntType<Width>
{
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_value_lt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_lt(&*lhs_value, *rhs_value)
}
fn cmp_value_le(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_le(&*lhs_value, *rhs_value)
}
fn cmp_value_gt(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_gt(&*lhs_value, *rhs_value)
}
fn cmp_value_ge(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: $UIntInRangeType<Start, End>,
rhs_value: Cow<'_, <$UIntInRangeType<Start, End> as Type>::SimValue>,
) -> bool {
HdlPartialOrd::cmp_ge(&*lhs_value, *rhs_value)
}
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_lt(rhs.cast_to_bits())
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_le(rhs.cast_to_bits())
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_gt(rhs.cast_to_bits())
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<$UIntInRangeType<Start, End>>) -> Expr<Bool> {
lhs.cmp_ge(rhs.cast_to_bits())
}
}

View file

@ -7,7 +7,10 @@ use crate::{
array::{Array, ArrayType},
bundle::{Bundle, BundleType},
clock::Clock,
expr::{Expr, Flow, ToExpr, ToLiteralBits, ops::BundleLiteral, repeat},
expr::{
Expr, Flow, ToExpr, ToLiteralBits, ValueType, ops::BundleLiteral, repeat,
value_category::ValueCategoryExpr,
},
hdl,
int::{Bool, DynSize, Size, UInt, UIntType},
intern::{Intern, Interned},
@ -366,10 +369,16 @@ impl<T: PortType> fmt::Debug for MemPort<T> {
}
}
impl<T: PortType> MemPort<T> {
pub fn ty(&self) -> T::Port {
impl<T: PortType> ValueType for MemPort<T> {
type Type = T::Port;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> T::Port {
T::port_ty(self)
}
}
impl<T: PortType> MemPort<T> {
pub fn source_location(&self) -> SourceLocation {
self.source_location
}
@ -830,7 +839,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
depth: Option<usize>,
initial_value: Expr<Array>,
) -> Interned<BitSlice> {
let initial_value_ty = Expr::ty(initial_value);
let initial_value_ty = initial_value.ty();
assert_eq!(
*mem_element_type,
Element::from_canonical(initial_value_ty.element()),
@ -1011,7 +1020,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
target.depth,
initial_value,
));
target.depth = Some(Expr::ty(initial_value).len());
target.depth = Some(initial_value.ty().len());
}
#[track_caller]
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {

View file

@ -8,12 +8,13 @@ use crate::{
clock::{Clock, ClockDomain},
enum_::{Enum, EnumMatchVariantsIter, EnumType},
expr::{
Expr, Flow, ToExpr,
Expr, Flow, ToExpr, ValueType,
ops::VariantAccess,
target::{
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
TargetPathElement,
},
value_category::ValueCategoryExpr,
},
formal::FormalKind,
int::{Bool, DynSize, Size},
@ -205,7 +206,7 @@ impl StmtConnect {
source_location,
} = *self;
assert!(
Expr::ty(lhs).can_connect(Expr::ty(rhs)),
lhs.ty().can_connect(rhs.ty()),
"can't connect types that are not equivalent:\nlhs type:\n{lhs_orig_ty:?}\nrhs type:\n{rhs_orig_ty:?}\nat: {source_location}",
);
assert!(
@ -219,14 +220,14 @@ impl StmtConnect {
match Expr::flow(rhs) {
Flow::Source | Flow::Duplex => {}
Flow::Sink => assert!(
Expr::ty(rhs).is_passive(),
rhs.ty().is_passive(),
"can't connect from sink with non-passive type\nat: {source_location}"
),
}
}
#[track_caller]
fn assert_validity(&self) {
self.assert_validity_with_original_types(Expr::ty(self.lhs), Expr::ty(self.rhs));
self.assert_validity_with_original_types(self.lhs.ty(), self.rhs.ty());
}
}
@ -331,7 +332,7 @@ impl Copy for StmtMatch {}
impl StmtMatch {
#[track_caller]
fn assert_validity(&self) {
assert_eq!(Expr::ty(self.expr).variants().len(), self.blocks.len());
assert_eq!(self.expr.ty().variants().len(), self.blocks.len());
}
}
@ -765,6 +766,15 @@ impl<T: BundleType> fmt::Debug for Instance<T> {
}
}
impl<T: BundleType> ValueType for Instance<T> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> T {
self.instantiated.io_ty()
}
}
impl<T: BundleType> Instance<T> {
pub fn canonical(self) -> Instance<Bundle> {
let Self {
@ -828,9 +838,6 @@ impl<T: BundleType> Instance<T> {
pub fn must_connect_to(&self) -> bool {
true
}
pub fn ty(&self) -> T {
self.instantiated.io_ty()
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
@ -2028,7 +2035,7 @@ impl<CD> RegBuilder<CD, (), ()> {
init: _,
ty: _,
} = self;
let ty = Expr::ty(init);
let ty = init.ty();
RegBuilder {
name,
source_location,
@ -2597,7 +2604,7 @@ pub fn enum_match_variants_helper<T: EnumType>(
ModuleBuilder::with(|m| {
let mut impl_ = m.impl_.borrow_mut();
let body = impl_.body.builder_normal_body();
let enum_ty = Expr::ty(base);
let enum_ty = base.ty();
let outer_block: BlockId = m.block_stack.top();
let blocks = Interned::from_iter(enum_ty.variants().iter().map(|_| body.new_block()));
body.block(outer_block).stmts.push(
@ -2649,7 +2656,7 @@ pub fn connect_any_with_loc<Lhs: ToExpr, Rhs: ToExpr>(
rhs,
source_location,
};
connect.assert_validity_with_original_types(Expr::ty(lhs_orig), Expr::ty(rhs_orig));
connect.assert_validity_with_original_types(lhs_orig.ty(), rhs_orig.ty());
ModuleBuilder::with(|m| {
m.impl_
.borrow_mut()
@ -2764,7 +2771,7 @@ pub fn memory_with_init_and_loc<Element: Type, Len: Size>(
source_location: SourceLocation,
) -> MemBuilder<Element, Len> {
let initial_value = initial_value.to_expr();
let mut retval = memory_array_with_loc(name, Expr::ty(initial_value), source_location);
let mut retval = memory_array_with_loc(name, initial_value.ty(), source_location);
retval.initial_value(initial_value);
retval
}
@ -2807,6 +2814,15 @@ pub struct ModuleIO<T: Type> {
source_location: SourceLocation,
}
impl<T: Type> ValueType for ModuleIO<T> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type> fmt::Debug for ModuleIO<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ModuleIO")
@ -2908,9 +2924,6 @@ impl<T: Type> ModuleIO<T> {
unreachable!()
}
}
pub fn ty(&self) -> T {
self.ty
}
}
#[derive(PartialEq, Eq, Hash, Clone, Copy)]

View file

@ -6,7 +6,7 @@ use crate::{
bundle::{BundleField, BundleType},
enum_::{EnumType, EnumVariant},
expr::{
ExprEnum,
ExprEnum, ValueType,
ops::{self, ArrayLiteral},
target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
@ -521,7 +521,7 @@ impl State {
Entry::Vacant(entry) => (
*entry.insert(Resets::with_new_nodes(
&mut self.reset_graph,
Expr::ty(expr),
expr.ty(),
source_location,
)),
true,
@ -1020,7 +1020,7 @@ fn cast_bit_op<P: Pass, T: Type, A: Type>(
}
macro_rules! match_arg_ty {
($($Variant:ident),*) => {
*match Expr::ty(arg) {
*match arg.ty() {
CanonicalType::Array(_)
| CanonicalType::Enum(_)
| CanonicalType::Bundle(_)
@ -1660,7 +1660,8 @@ impl RunPassDispatch for AnyReg {
let clock_domain = Expr::<Bundle>::from_canonical(
Expr::canonical(reg.clock_domain()).run_pass(pass_args)?.0,
);
match Expr::ty(clock_domain)
match clock_domain
.ty()
.field_by_name("rst".intern())
.expect("ClockDomain has rst field")
.ty

View file

@ -5,7 +5,7 @@ use crate::{
bundle::{Bundle, BundleField, BundleType},
enum_::{Enum, EnumType, EnumVariant},
expr::{
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr,
CastBitsTo, CastTo, CastToBits, Expr, ExprEnum, HdlPartialEq, ToExpr, ValueType,
ops::{self, EnumLiteral},
},
hdl,
@ -528,7 +528,7 @@ fn connect_port(
rhs: Expr<CanonicalType>,
source_location: SourceLocation,
) {
if Expr::ty(lhs) == Expr::ty(rhs) {
if lhs.ty() == rhs.ty() {
stmts.push(
StmtConnect {
lhs,
@ -539,7 +539,7 @@ fn connect_port(
);
return;
}
match (Expr::ty(lhs), Expr::ty(rhs)) {
match (lhs.ty(), rhs.ty()) {
(CanonicalType::Bundle(lhs_type), CanonicalType::UInt(_) | CanonicalType::Bool(_)) => {
let lhs = Expr::<Bundle>::from_canonical(lhs);
for field in lhs_type.fields() {
@ -586,8 +586,8 @@ fn connect_port(
| (CanonicalType::PhantomConst(_), _)
| (CanonicalType::DynSimOnly(_), _) => unreachable!(
"trying to connect memory ports:\n{:?}\n{:?}",
Expr::ty(lhs),
Expr::ty(rhs),
lhs.ty(),
rhs.ty(),
),
}
}
@ -607,14 +607,17 @@ fn match_int_tag(
};
};
let mut retval = StmtIf {
cond: int_tag_expr
.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(next_to_last_variant_index)),
cond: int_tag_expr.cmp_eq(
int_tag_expr
.ty()
.from_int_wrapping(next_to_last_variant_index),
),
source_location,
blocks: [next_to_last_block, last_block],
};
for (variant_index, block) in blocks_iter.rev() {
retval = StmtIf {
cond: int_tag_expr.cmp_eq(Expr::ty(int_tag_expr).from_int_wrapping(variant_index)),
cond: int_tag_expr.cmp_eq(int_tag_expr.ty().from_int_wrapping(variant_index)),
source_location,
blocks: [
block,
@ -657,7 +660,7 @@ impl Folder for State {
ExprEnum::VariantAccess(op) => {
let folded_base_expr = Expr::canonical(op.base()).fold(self)?;
Ok(*Expr::expr_enum(self.handle_variant_access(
Expr::ty(op.base()),
op.base().ty(),
folded_base_expr,
op.variant_index(),
)?))
@ -867,7 +870,7 @@ impl Folder for State {
}) => {
let folded_expr = Expr::canonical(expr).fold(self)?;
let folded_blocks = blocks.fold(self)?;
self.handle_match(Expr::ty(expr), folded_expr, source_location, &folded_blocks)
self.handle_match(expr.ty(), folded_expr, source_location, &folded_blocks)
}
Stmt::Connect(StmtConnect {
lhs,
@ -878,8 +881,8 @@ impl Folder for State {
let folded_rhs = rhs.fold(self)?;
let mut output_stmts = vec![];
self.handle_stmt_connect(
Expr::ty(lhs),
Expr::ty(rhs),
lhs.ty(),
rhs.ty(),
folded_lhs,
folded_rhs,
source_location,

View file

@ -4,7 +4,7 @@ use crate::{
annotations::TargetedAnnotation,
array::Array,
bundle::{Bundle, BundleType},
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr, ValueType},
int::{Bool, SInt, Size, UInt},
intern::{Intern, Interned},
memory::{Mem, MemPort, PortType},
@ -530,7 +530,7 @@ impl ModuleState {
connect_read(
output_stmts,
wire_read,
Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
Expr::<UInt>::from_canonical(port_read).cast_bits_to(wire_read.ty()),
);
};
let connect_write_enum =

View file

@ -11,7 +11,7 @@ use crate::{
clock::Clock,
enum_::{Enum, EnumType, EnumVariant},
expr::{
Expr, ExprEnum, ops,
Expr, ExprEnum, ValueType, ops,
target::{
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
TargetPathDynArrayElement, TargetPathElement,

View file

@ -2,13 +2,10 @@
// See Notices.txt for copyright information
use crate::{
expr::{
Expr, ToExpr,
ops::{ExprPartialEq, ExprPartialOrd},
},
expr::{Expr, HdlPartialEqImpl, HdlPartialOrdImpl, ToExpr, ValueType},
int::Bool,
intern::{Intern, Interned, InternedCompare, LazyInterned, LazyInternedTrait, Memoize},
sim::value::{SimValue, SimValuePartialEq, ToSimValue, ToSimValueWithType},
sim::value::{SimValue, ToSimValue, ToSimValueWithType},
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten,
@ -22,6 +19,7 @@ use serde::{
};
use std::{
any::Any,
borrow::Cow,
fmt,
hash::{Hash, Hasher},
marker::PhantomData,
@ -372,50 +370,102 @@ impl<'de, T: ?Sized + PhantomConstValue> Deserialize<'de> for PhantomConst<T> {
}
}
impl<T: ?Sized + PhantomConstValue> ExprPartialEq<Self> for PhantomConst<T> {
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> ExprPartialOrd<Self> for PhantomConst<T> {
fn cmp_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
fn cmp_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
fn cmp_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
false.to_expr()
}
fn cmp_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(Expr::ty(lhs), Expr::ty(rhs));
true.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> SimValuePartialEq<Self> for PhantomConst<T> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
assert_eq!(SimValue::ty(this), SimValue::ty(other));
impl<T: ?Sized + PhantomConstValue> HdlPartialEqImpl<Self> for PhantomConst<T> {
#[track_caller]
fn cmp_value_eq(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
true
}
#[track_caller]
fn cmp_expr_eq(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr()
}
#[track_caller]
fn cmp_expr_ne(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> HdlPartialOrdImpl<Self> for PhantomConst<T> {
#[track_caller]
fn cmp_value_lt(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
false
}
#[track_caller]
fn cmp_value_le(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
true
}
#[track_caller]
fn cmp_value_gt(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
false
}
#[track_caller]
fn cmp_value_ge(
lhs: Self,
_lhs_value: Cow<'_, Self::SimValue>,
rhs: Self,
_rhs_value: Cow<'_, <Self as Type>::SimValue>,
) -> bool {
assert_eq!(lhs, rhs);
true
}
#[track_caller]
fn cmp_expr_lt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr()
}
#[track_caller]
fn cmp_expr_le(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr()
}
#[track_caller]
fn cmp_expr_gt(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
false.to_expr()
}
#[track_caller]
fn cmp_expr_ge(lhs: Expr<Self>, rhs: Expr<Self>) -> Expr<Bool> {
assert_eq!(lhs.ty(), rhs.ty());
true.to_expr()
}
}
impl<T: ?Sized + PhantomConstValue> ToSimValue for PhantomConst<T> {
type Type = PhantomConst<T>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(*self, *self)
}
@ -479,7 +529,7 @@ impl_phantom_const_get! {
impl_phantom_const_get! {
impl PhantomConstGet<T> for Expr<PhantomConst<T>> {
fn get(&self) -> _ {
PhantomConst::get(Expr::ty(*self))
PhantomConst::get(self.ty())
}
}
}

View file

@ -3,7 +3,7 @@
use crate::{
bundle::{Bundle, BundleField, BundleType},
expr::{Expr, ExprEnum},
expr::{Expr, ExprEnum, ValueType},
intern::{Intern, Interned},
module::{Module, ModuleBuilder, ModuleIO, connect_with_loc, instance_with_loc, wire_with_loc},
source_location::SourceLocation,
@ -615,8 +615,8 @@ impl<T: Type> Peripheral<T> {
};
drop(state);
let Self { ty, common } = self;
let instance_io_field = Expr::field(output, &common.id.name);
assert_eq!(ty, Expr::ty(instance_io_field));
let instance_io_field = Expr::field::<T>(output, &common.id.name);
assert_eq!(ty, instance_io_field.ty());
Ok(UsedPeripheral {
instance_io_field,
common,
@ -654,7 +654,7 @@ impl<T: Type> UsedPeripheral<T> {
ref common,
} = *self;
PeripheralRef {
ty: Expr::ty(instance_io_field),
ty: instance_io_field.ty(),
common,
}
}
@ -915,7 +915,7 @@ impl<'a, T: Type> PeripheralRef<'a, T> {
main_module_io_fields.push(BundleField {
name: self.name(),
flipped: self.is_input(),
ty: Expr::ty(canonical_wire),
ty: canonical_wire.ty(),
});
on_use_function(&mut **shared_state, self.canonical(), canonical_wire);
drop(on_use_state);

View file

@ -13,7 +13,7 @@ pub use crate::{
enum_::{Enum, HdlNone, HdlOption, HdlSome},
expr::{
CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
ReduceBits, ToExpr, repeat,
ReduceBits, ToExpr, ValueType, repeat,
},
formal::{
MakeFormalExpr, all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset,

View file

@ -2,7 +2,7 @@
// See Notices.txt for copyright information
use crate::{
clock::ClockDomain,
expr::{Expr, Flow},
expr::{Expr, Flow, ValueType, value_category::ValueCategoryExpr},
intern::Interned,
module::{NameId, ScopedNameId},
reset::{Reset, ResetType},
@ -20,7 +20,16 @@ pub struct Reg<T: Type, R: ResetType = Reset> {
init: Option<Expr<T>>,
}
impl<T: Type + fmt::Debug, R: ResetType> fmt::Debug for Reg<T, R> {
impl<T: Type, R: ResetType> ValueType for Reg<T, R> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type, R: ResetType> fmt::Debug for Reg<T, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
name,
@ -68,7 +77,7 @@ impl<T: Type, R: ResetType> Reg<T, R> {
"register type must be a storable type"
);
if let Some(init) = init {
assert_eq!(ty, Expr::ty(init), "register's type must match init type");
assert_eq!(ty, init.ty(), "register's type must match init type");
}
Self {
name: scoped_name,
@ -78,9 +87,6 @@ impl<T: Type, R: ResetType> Reg<T, R> {
init,
}
}
pub fn ty(&self) -> T {
self.ty
}
pub fn source_location(&self) -> SourceLocation {
self.source_location
}

View file

@ -2,13 +2,15 @@
// See Notices.txt for copyright information
use crate::{
clock::Clock,
expr::{Expr, ToExpr, ops},
int::{Bool, SInt, UInt},
expr::{CastToImpl, Expr, ValueType},
int::{Bool, SInt, SIntValue, UInt, UIntValue},
sim::value::SimValue,
source_location::SourceLocation,
ty::{
CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter,
OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self,
},
util::ConstUsize,
};
use bitvec::{bits, order::Lsb0};
@ -19,15 +21,15 @@ mod sealed {
pub trait ResetType:
StaticType<MaskType = Bool>
+ sealed::ResetTypeSealed
+ ops::ExprCastTo<Bool>
+ ops::ExprCastTo<Reset>
+ ops::ExprCastTo<SyncReset>
+ ops::ExprCastTo<AsyncReset>
+ ops::ExprCastTo<Clock>
+ ops::ExprCastTo<UInt<1>>
+ ops::ExprCastTo<SInt<1>>
+ ops::ExprCastTo<UInt>
+ ops::ExprCastTo<SInt>
+ CastToImpl<Bool, ValueOutput = bool>
+ CastToImpl<Reset, ValueOutput = SimValue<Reset>>
+ CastToImpl<SyncReset, ValueOutput = SimValue<SyncReset>>
+ CastToImpl<AsyncReset, ValueOutput = SimValue<AsyncReset>>
+ CastToImpl<Clock, ValueOutput = SimValue<Clock>>
+ CastToImpl<UInt<1>, ValueOutput = UIntValue<ConstUsize<1>>>
+ CastToImpl<SInt<1>, ValueOutput = SIntValue<ConstUsize<1>>>
+ CastToImpl<UInt, ValueOutput = UIntValue>
+ CastToImpl<SInt, ValueOutput = SIntValue>
{
fn dispatch<D: ResetTypeDispatch>(input: D::Input<Self>, dispatch: D) -> D::Output<Self>;
}
@ -132,29 +134,34 @@ macro_rules! reset_type {
}
pub trait $Trait {
fn $trait_fn(&self) -> Expr<$name>;
type Output: ValueType<Type = $name>;
fn $trait_fn(&self) -> Self::Output;
}
impl<T: ?Sized + $Trait> $Trait for &'_ T {
fn $trait_fn(&self) -> Expr<$name> {
type Output = T::Output;
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn()
}
}
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
fn $trait_fn(&self) -> Expr<$name> {
type Output = T::Output;
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn()
}
}
impl<T: ?Sized + $Trait> $Trait for Box<T> {
fn $trait_fn(&self) -> Expr<$name> {
type Output = T::Output;
fn $trait_fn(&self) -> Self::Output {
(**self).$trait_fn()
}
}
$($impl_trait $Trait for Expr<$name> {
fn $trait_fn(&self) -> Expr<$name> {
type Output = Expr<$name>;
fn $trait_fn(&self) -> Self::Output {
*self
}
})?
@ -171,13 +178,15 @@ reset_type!(
);
impl ToSyncReset for bool {
fn to_sync_reset(&self) -> Expr<SyncReset> {
self.to_expr().to_sync_reset()
type Output = SimValue<SyncReset>;
fn to_sync_reset(&self) -> Self::Output {
SimValue::from_value(SyncReset, *self)
}
}
impl ToAsyncReset for bool {
fn to_async_reset(&self) -> Expr<AsyncReset> {
self.to_expr().to_async_reset()
type Output = SimValue<AsyncReset>;
fn to_async_reset(&self) -> Self::Output {
SimValue::from_value(AsyncReset, *self)
}
}

View file

@ -6,7 +6,7 @@
use crate::{
bundle::{BundleField, BundleType},
expr::{
Flow, ToLiteralBits,
Flow,
target::{
GetTarget, Target, TargetPathArrayElement, TargetPathBundleField, TargetPathElement,
},
@ -47,7 +47,6 @@ use num_bigint::BigInt;
use num_traits::{Signed, Zero};
use std::{
any::Any,
borrow::Cow,
cell::{Cell, RefCell},
collections::{BTreeMap, BTreeSet},
fmt,
@ -1504,9 +1503,10 @@ impl<I: BoolOrIntType> MaybeNeedsSettleFn<&'_ mut interpreter::State> for ReadBo
fn call(self, state: &mut interpreter::State) -> Self::Output {
let Self { compiled_value, io } = self;
match compiled_value.range.len().as_single() {
Some(TypeLenSingle::SmallSlot) => Expr::ty(io)
Some(TypeLenSingle::SmallSlot) => io
.ty()
.value_from_int_wrapping(state.small_slots[compiled_value.range.small_slots.start]),
Some(TypeLenSingle::BigSlot) => Expr::ty(io).value_from_int_wrapping(
Some(TypeLenSingle::BigSlot) => io.ty().value_from_int_wrapping(
state.big_slots[compiled_value.range.big_slots.start].clone(),
),
Some(TypeLenSingle::SimOnlySlot) | None => unreachable!(),
@ -2889,7 +2889,7 @@ impl SimulationImpl {
},
);
}
let size = Expr::ty(io).size();
let size = io.ty().size();
let mut opaque = OpaqueSimValue::with_capacity(size);
opaque.rewrite_with(size, |mut writer| {
SimulationImpl::read_write_sim_value_helper(
@ -2922,7 +2922,7 @@ impl SimulationImpl {
);
writer.fill_cloned_from_slice(OpaqueSimValueSlice::empty())
});
SimValue::from_opaque(Expr::ty(io), opaque)
SimValue::from_opaque(io.ty(), opaque)
}
/// doesn't modify `opaque`
fn value_changed(
@ -3006,7 +3006,7 @@ impl SimulationImpl {
.get_module_mut(which_module)
.write_helper(io, which_module);
self.event_queue.add_event_for_now(EventKind::State);
assert_eq!(Expr::ty(io), SimValue::ty(value));
assert_eq!(io.ty(), value.ty());
Self::read_write_sim_value_helper(
&mut self.state,
compiled_value,
@ -3269,16 +3269,12 @@ macro_rules! impl_simulation_methods {
pub $($async)? fn write_bool_or_int<I: BoolOrIntType>(
&mut $self,
io: Expr<I>,
value: impl ToExpr<Type = I>,
value: impl ToSimValueWithType<I>,
) {
let value = value.to_expr();
assert_eq!(Expr::ty(io), Expr::ty(value), "type mismatch");
let value = value
.to_literal_bits()
.expect("the value that is being written to an input must be a literal");
let value = value.to_sim_value_with_type(io.ty());
$self.sim_impl.borrow_mut().write_bool_or_int(
io,
I::bits_to_value(Cow::Borrowed(&value)),
SimValue::into_value(value),
$which_module,
);
}
@ -3343,7 +3339,7 @@ macro_rules! impl_simulation_methods {
pub $($async)? fn write<IO: Type, V: value::ToSimValueWithType<IO>>(&mut $self, io: Expr<IO>, value: V) {
$self.sim_impl.borrow_mut().write(
Expr::canonical(io),
&SimValue::into_canonical(value.into_sim_value_with_type(Expr::ty(io))),
&SimValue::into_canonical(value.into_sim_value_with_type(io.ty())),
$which_module,
);
}

View file

@ -7,7 +7,7 @@ use crate::{
bundle::{BundleField, BundleType},
enum_::{EnumType, EnumVariant},
expr::{
ExprEnum, Flow, ops,
ExprEnum, Flow, ValueType, ops,
target::{
GetTarget, Target, TargetBase, TargetPathArrayElement, TargetPathBundleField,
TargetPathElement,
@ -1714,7 +1714,7 @@ impl MakeTraceDeclTarget {
}
fn ty(self) -> CanonicalType {
match self {
MakeTraceDeclTarget::Expr(expr) => Expr::ty(expr),
MakeTraceDeclTarget::Expr(expr) => expr.ty(),
MakeTraceDeclTarget::Memory { ty, .. } => ty,
}
}
@ -2771,7 +2771,7 @@ impl Compiler {
let retval = parts
.into_iter()
.map(|part| part.cast_to_bits())
.reduce(|accumulator, part| accumulator | (part << Expr::ty(accumulator).width))
.reduce(|accumulator, part| accumulator | (part << accumulator.ty().width))
.unwrap_or_else(|| UInt[0].zero().to_expr());
let retval = self.compile_expr(instantiated_module, Expr::canonical(retval));
let retval = self
@ -2783,7 +2783,7 @@ impl Compiler {
instantiated_module: InstantiatedModule,
expr: ops::CastToBits,
) -> CompiledValue<UInt> {
match Expr::ty(expr.arg()) {
match expr.arg().ty() {
CanonicalType::UInt(_) => {
self.compile_cast_scalar_to_bits(instantiated_module, expr.arg(), |arg| arg)
}
@ -2949,7 +2949,7 @@ impl Compiler {
return retval;
}
let mut cast_bit = |arg: Expr<CanonicalType>| {
let src_signed = match Expr::ty(arg) {
let src_signed = match arg.ty() {
CanonicalType::UInt(_) => false,
CanonicalType::SInt(_) => true,
CanonicalType::Bool(_) => false,
@ -2963,7 +2963,7 @@ impl Compiler {
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
};
let dest_signed = match Expr::ty(expr) {
let dest_signed = match expr.ty() {
CanonicalType::UInt(_) => false,
CanonicalType::SInt(_) => true,
CanonicalType::Bool(_) => false,
@ -2977,22 +2977,23 @@ impl Compiler {
CanonicalType::PhantomConst(_) => unreachable!(),
CanonicalType::DynSimOnly(_) => unreachable!(),
};
self.simple_nary_big_expr(instantiated_module, Expr::ty(expr), [arg], |dest, [src]| {
match (src_signed, dest_signed) {
(false, false) | (true, true) => {
vec![Insn::Copy { dest, src }]
}
(false, true) => vec![Insn::CastToSInt {
dest,
src,
dest_width: 1,
}],
(true, false) => vec![Insn::CastToUInt {
dest,
src,
dest_width: 1,
}],
self.simple_nary_big_expr(instantiated_module, expr.ty(), [arg], |dest, [src]| match (
src_signed,
dest_signed,
) {
(false, false) | (true, true) => {
vec![Insn::Copy { dest, src }]
}
(false, true) => vec![Insn::CastToSInt {
dest,
src,
dest_width: 1,
}],
(true, false) => vec![Insn::CastToUInt {
dest,
src,
dest_width: 1,
}],
})
.into()
};
@ -3032,21 +3033,13 @@ impl Compiler {
})
.into(),
ExprEnum::PhantomConst(_) => self
.compile_aggregate_literal(instantiated_module, Expr::ty(expr), Interned::default())
.compile_aggregate_literal(instantiated_module, expr.ty(), Interned::default())
.into(),
ExprEnum::BundleLiteral(literal) => self
.compile_aggregate_literal(
instantiated_module,
Expr::ty(expr),
literal.field_values(),
)
.compile_aggregate_literal(instantiated_module, expr.ty(), literal.field_values())
.into(),
ExprEnum::ArrayLiteral(literal) => self
.compile_aggregate_literal(
instantiated_module,
Expr::ty(expr),
literal.element_values(),
)
.compile_aggregate_literal(instantiated_module, expr.ty(), literal.element_values())
.into(),
ExprEnum::EnumLiteral(expr) => {
let enum_bits_ty = UInt[expr.ty().type_properties().bit_width];
@ -3074,13 +3067,13 @@ impl Compiler {
ExprEnum::NotU(expr) => self
.simple_nary_big_expr(
instantiated_module,
Expr::ty(expr.arg()).canonical(),
expr.arg().ty().canonical(),
[Expr::canonical(expr.arg())],
|dest, [src]| {
vec![Insn::NotU {
dest,
src,
width: Expr::ty(expr.arg()).width(),
width: expr.arg().ty().width(),
}]
},
)
@ -3088,7 +3081,7 @@ impl Compiler {
ExprEnum::NotS(expr) => self
.simple_nary_big_expr(
instantiated_module,
Expr::ty(expr.arg()).canonical(),
expr.arg().ty().canonical(),
[Expr::canonical(expr.arg())],
|dest, [src]| vec![Insn::NotS { dest, src }],
)
@ -3096,7 +3089,7 @@ impl Compiler {
ExprEnum::NotB(expr) => self
.simple_nary_big_expr(
instantiated_module,
Expr::ty(expr.arg()).canonical(),
expr.arg().ty().canonical(),
[Expr::canonical(expr.arg())],
|dest, [src]| {
vec![Insn::NotU {
@ -3600,12 +3593,12 @@ impl Compiler {
.map_ty(Bundle::from_canonical)
.field_by_index(expr.field_index()),
ExprEnum::VariantAccess(variant_access) => {
let start = Expr::ty(variant_access.base()).discriminant_bit_width();
let len = Expr::ty(expr).bit_width();
let start = variant_access.base().ty().discriminant_bit_width();
let len = expr.ty().bit_width();
self.compile_expr(
instantiated_module,
variant_access.base().cast_to_bits()[start..start + len]
.cast_bits_to(Expr::ty(expr)),
.cast_bits_to(expr.ty()),
)
}
ExprEnum::ArrayIndex(expr) => self
@ -3627,45 +3620,39 @@ impl Compiler {
.map_ty(Array::from_canonical)
.element_dyn(index_slot)
}
ExprEnum::ReduceBitAndU(expr) => if Expr::ty(expr.arg()).width() == 0 {
ExprEnum::ReduceBitAndU(expr) => if expr.arg().ty().width() == 0 {
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
} else {
self.compile_expr(
instantiated_module,
Expr::canonical(
expr.arg()
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
),
Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))),
)
}
.into(),
ExprEnum::ReduceBitAndS(expr) => if Expr::ty(expr.arg()).width() == 0 {
ExprEnum::ReduceBitAndS(expr) => if expr.arg().ty().width() == 0 {
self.compile_expr(instantiated_module, Expr::canonical(true.to_expr()))
} else {
self.compile_expr(
instantiated_module,
Expr::canonical(
expr.arg()
.cmp_eq(Expr::ty(expr.arg()).from_int_wrapping(-1)),
),
Expr::canonical(expr.arg().cmp_eq(expr.arg().ty().from_int_wrapping(-1))),
)
}
.into(),
ExprEnum::ReduceBitOrU(expr) => if Expr::ty(expr.arg()).width() == 0 {
ExprEnum::ReduceBitOrU(expr) => if expr.arg().ty().width() == 0 {
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
} else {
self.compile_expr(
instantiated_module,
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))),
)
}
.into(),
ExprEnum::ReduceBitOrS(expr) => if Expr::ty(expr.arg()).width() == 0 {
ExprEnum::ReduceBitOrS(expr) => if expr.arg().ty().width() == 0 {
self.compile_expr(instantiated_module, Expr::canonical(false.to_expr()))
} else {
self.compile_expr(
instantiated_module,
Expr::canonical(expr.arg().cmp_ne(Expr::ty(expr.arg()).from_int_wrapping(0))),
Expr::canonical(expr.arg().cmp_ne(expr.arg().ty().from_int_wrapping(0))),
)
}
.into(),
@ -3678,7 +3665,7 @@ impl Compiler {
vec![Insn::ReduceBitXor {
dest,
src,
input_width: Expr::ty(expr.arg()).width(),
input_width: expr.arg().ty().width(),
}]
},
)
@ -3692,7 +3679,7 @@ impl Compiler {
vec![Insn::ReduceBitXor {
dest,
src,
input_width: Expr::ty(expr.arg()).width(),
input_width: expr.arg().ty().width(),
}]
},
)
@ -3790,8 +3777,8 @@ impl Compiler {
mut rhs: Expr<CanonicalType>,
source_location: SourceLocation,
) {
if Expr::ty(lhs) != Expr::ty(rhs) || !Expr::ty(lhs).is_passive() {
match Expr::ty(lhs) {
if lhs.ty() != rhs.ty() || !lhs.ty().is_passive() {
match lhs.ty() {
CanonicalType::UInt(lhs_ty) => {
rhs = Expr::canonical(Expr::<UInt>::from_canonical(rhs).cast_to(lhs_ty));
}
@ -3800,7 +3787,7 @@ impl Compiler {
}
CanonicalType::Bool(_) => unreachable!(),
CanonicalType::Array(lhs_ty) => {
let CanonicalType::Array(rhs_ty) = Expr::ty(rhs) else {
let CanonicalType::Array(rhs_ty) = rhs.ty() else {
unreachable!();
};
assert_eq!(lhs_ty.len(), rhs_ty.len());
@ -3820,13 +3807,13 @@ impl Compiler {
return;
}
CanonicalType::Enum(lhs_ty) => {
let CanonicalType::Enum(rhs_ty) = Expr::ty(rhs) else {
let CanonicalType::Enum(rhs_ty) = rhs.ty() else {
unreachable!();
};
todo!("handle connect with different enum types");
}
CanonicalType::Bundle(lhs_ty) => {
let CanonicalType::Bundle(rhs_ty) = Expr::ty(rhs) else {
let CanonicalType::Bundle(rhs_ty) = rhs.ty() else {
unreachable!();
};
assert_eq!(lhs_ty.fields().len(), rhs_ty.fields().len());

View file

@ -2,6 +2,7 @@
// See Notices.txt for copyright information
use crate::{
expr::ValueType,
int::{BoolOrIntType, SInt, UInt},
intern::{Intern, Interned, Memoize},
sim::interpreter::parts::{

View file

@ -6,7 +6,10 @@ use crate::{
bundle::{Bundle, BundleType},
clock::Clock,
enum_::{Enum, EnumType},
expr::{CastBitsTo, Expr, ToExpr},
expr::{
CastBitsTo, Expr, HdlPartialEq, HdlPartialEqImpl, HdlPartialOrd, ToExpr, ValueType,
value_category::{ValueCategorySimValue, ValueCategoryValue},
},
int::{Bool, IntType, KnownSize, SInt, SIntType, SIntValue, Size, UInt, UIntType, UIntValue},
reset::{AsyncReset, Reset, SyncReset},
source_location::SourceLocation,
@ -23,10 +26,11 @@ use bitvec::{slice::BitSlice, vec::BitVec};
use hashbrown::hash_map::Entry;
use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error as _, ser::Error as _};
use std::{
borrow::Cow,
borrow::{Borrow, BorrowMut, Cow},
fmt::{self, Write},
hash::{BuildHasher, Hash, Hasher, RandomState},
ops::{Deref, DerefMut},
num::NonZero,
ops::{Deref, DerefMut, Index, IndexMut},
sync::{Arc, Mutex},
};
@ -136,7 +140,7 @@ impl<T: Type<SimValue: Serialize> + Serialize> Serialize for SimValue<T> {
S: Serializer,
{
SerdeSimValue {
ty: SimValue::ty(self),
ty: self.ty(),
value: std::borrow::Cow::Borrowed(&*self),
}
.serialize(serializer)
@ -157,7 +161,22 @@ pub struct SimValue<T: Type> {
inner: AlternatingCell<SimValueInner<T>>,
}
impl<T: Type + Clone> Clone for SimValue<T> {
impl<T: Type> ValueType for SimValue<T> {
type Type = T;
type ValueCategory = ValueCategorySimValue;
fn ty(&self) -> Self::Type {
self.inner.share().ty
}
}
impl<T: Type<SimValue: fmt::Display>> fmt::Display for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
T::SimValue::fmt(self, f)
}
}
impl<T: Type> Clone for SimValue<T> {
fn clone(&self) -> Self {
Self {
inner: AlternatingCell::new_unique(self.inner.share().clone()),
@ -212,9 +231,6 @@ impl<T: Type> SimValue<T> {
inner: AlternatingCell::new_unique(inner),
}
}
pub fn ty(this: &Self) -> T {
this.inner.share().ty
}
pub fn into_opaque(this: Self) -> OpaqueSimValue {
this.inner.into_inner().into_opaque()
}
@ -253,7 +269,7 @@ impl<T: Type> SimValue<T> {
SimValue::from_opaque(ty.canonical(), opaque)
}
pub fn canonical(this: &Self) -> SimValue<CanonicalType> {
SimValue::from_opaque(Self::ty(this).canonical(), Self::opaque(this).clone())
SimValue::from_opaque(this.ty().canonical(), Self::opaque(this).clone())
}
#[track_caller]
pub fn from_dyn_int(v: SimValue<T::Dyn>) -> Self
@ -274,7 +290,7 @@ impl<T: Type> SimValue<T> {
where
T: IntType,
{
SimValue::from_opaque(Self::ty(this).as_dyn_int(), Self::opaque(&this).clone())
SimValue::from_opaque(this.ty().as_dyn_int(), Self::opaque(&this).clone())
}
#[track_caller]
pub fn from_bundle(v: SimValue<Bundle>) -> Self
@ -296,7 +312,7 @@ impl<T: Type> SimValue<T> {
T: BundleType,
{
SimValue::from_opaque(
Bundle::from_canonical(Self::ty(this).canonical()),
Bundle::from_canonical(this.ty().canonical()),
Self::opaque(&this).clone(),
)
}
@ -320,7 +336,7 @@ impl<T: Type> SimValue<T> {
T: EnumType,
{
SimValue::from_opaque(
Enum::from_canonical(Self::ty(this).canonical()),
Enum::from_canonical(this.ty().canonical()),
Self::opaque(&this).clone(),
)
}
@ -342,17 +358,11 @@ impl<T: Type> DerefMut for SimValue<T> {
impl<T: Type> fmt::Debug for SimValue<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let inner = self.inner.share();
f.debug_struct("SimValue")
.field("ty", &inner.ty)
.field("value", &inner.value)
.finish()
T::SimValue::fmt(&**self, f)
}
}
impl<T: Type> ToExpr for SimValue<T> {
type Type = T;
#[track_caller]
fn to_expr(&self) -> Expr<Self::Type> {
let inner = self.inner.share();
@ -360,67 +370,297 @@ impl<T: Type> ToExpr for SimValue<T> {
inner.sim_only_values_len, 0,
"can't convert sim-only values to Expr"
);
inner.opaque.bits().cast_bits_to(inner.ty)
inner.opaque.bits().to_expr().cast_bits_to(inner.ty)
}
}
pub trait SimValuePartialEq<T: Type = Self>: Type {
#[track_caller]
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<T>) -> bool;
impl<T: Type, Len: Size, I> Index<I> for SimValue<ArrayType<T, Len>>
where
[SimValue<T>]: Index<I>,
{
type Output = <[SimValue<T>] as Index<I>>::Output;
fn index(&self, index: I) -> &Self::Output {
(**self).borrow().index(index)
}
}
impl<T: SimValuePartialEq<U>, U: Type> PartialEq<SimValue<U>> for SimValue<T> {
impl<T: Type, Len: Size, I> IndexMut<I> for SimValue<ArrayType<T, Len>>
where
[SimValue<T>]: IndexMut<I>,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
(**self).borrow_mut().index_mut(index)
}
}
impl<T: Type, Len: Size> SimValue<ArrayType<T, Len>> {
pub fn iter(&self) -> std::slice::Iter<'_, SimValue<T>> {
(**self).borrow().iter()
}
pub fn iter_mut(&mut self) -> std::slice::IterMut<'_, SimValue<T>> {
(**self).borrow_mut().iter_mut()
}
}
impl<T: Type, Len: Size> IntoIterator for SimValue<ArrayType<T, Len>> {
type Item = SimValue<T>;
type IntoIter = std::vec::IntoIter<SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
Vec::into_iter(Self::into_value(self).into())
}
}
impl<'a, T: Type, Len: Size> IntoIterator for &'a SimValue<ArrayType<T, Len>> {
type Item = &'a SimValue<T>;
type IntoIter = std::slice::Iter<'a, SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl<'a, T: Type, Len: Size> IntoIterator for &'a mut SimValue<ArrayType<T, Len>> {
type Item = &'a mut SimValue<T>;
type IntoIter = std::slice::IterMut<'a, SimValue<T>>;
fn into_iter(self) -> Self::IntoIter {
self.iter_mut()
}
}
impl<T: Type, Len: Size> AsRef<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
fn as_ref(&self) -> &[SimValue<T>] {
(**self).as_ref()
}
}
impl<T: Type, Len: Size> AsMut<[SimValue<T>]> for SimValue<ArrayType<T, Len>> {
fn as_mut(&mut self) -> &mut [SimValue<T>] {
(**self).as_mut()
}
}
impl<T: Type, U: Type> PartialEq<SimValue<U>> for SimValue<T>
where
Self: for<'a> HdlPartialEq<&'a SimValue<U>, Output = SimValue<Bool>>,
{
#[track_caller]
fn eq(&self, other: &SimValue<U>) -> bool {
T::sim_value_eq(self, other)
*self.cmp_eq(other)
}
#[track_caller]
fn ne(&self, other: &SimValue<U>) -> bool {
*self.cmp_ne(other)
}
}
impl<Width: Size> SimValuePartialEq<Self> for UIntType<Width> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
pub trait SimValueEq: Type
where
for<'a> SimValue<Self>: HdlPartialEq<&'a SimValue<Self>, Output = SimValue<Bool>>,
{
}
impl<T: SimValueEq> Eq for SimValue<T> where
for<'a> SimValue<T>: HdlPartialEq<&'a SimValue<T>, Output = SimValue<Bool>>
{
}
impl<T: Type, U: Type> PartialOrd<SimValue<U>> for SimValue<T>
where
Self: for<'a> HdlPartialOrd<&'a SimValue<U>, Output = SimValue<Bool>>,
{
#[track_caller]
fn partial_cmp(&self, other: &SimValue<U>) -> Option<std::cmp::Ordering> {
if *self.cmp_eq(other) {
Some(std::cmp::Ordering::Equal)
} else if *self.cmp_lt(other) {
Some(std::cmp::Ordering::Less)
} else if *self.cmp_gt(other) {
Some(std::cmp::Ordering::Greater)
} else {
None
}
}
#[track_caller]
fn lt(&self, other: &SimValue<U>) -> bool {
*self.cmp_lt(other)
}
#[track_caller]
fn le(&self, other: &SimValue<U>) -> bool {
*self.cmp_le(other)
}
#[track_caller]
fn gt(&self, other: &SimValue<U>) -> bool {
*self.cmp_gt(other)
}
#[track_caller]
fn ge(&self, other: &SimValue<U>) -> bool {
*self.cmp_ge(other)
}
}
impl<Width: Size> SimValuePartialEq<Self> for SIntType<Width> {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
pub trait SimValueOrd: SimValueEq
where
for<'a> SimValue<Self>: HdlPartialOrd<&'a SimValue<Self>, Output = SimValue<Bool>>,
{
}
impl<T: SimValueOrd> Ord for SimValue<T>
where
for<'a> SimValue<T>: HdlPartialOrd<&'a SimValue<T>, Output = SimValue<Bool>>,
{
#[track_caller]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
if *self.cmp_eq(other) {
std::cmp::Ordering::Equal
} else if *self.cmp_lt(other) {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
}
}
impl SimValuePartialEq<Bool> for Bool {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Bool>) -> bool {
**this == **other
impl<Width: Size> SimValueEq for UIntType<Width> {}
impl<Width: Size> SimValueOrd for UIntType<Width> {}
impl<Width: Size> SimValueEq for SIntType<Width> {}
impl<Width: Size> SimValueOrd for SIntType<Width> {}
macro_rules! impl_sim_value_cmp_as_bool {
($ty:ident) => {
impl SimValueEq for $ty {}
impl SimValueOrd for $ty {}
};
}
impl_sim_value_cmp_as_bool!(Bool);
impl_sim_value_cmp_as_bool!(Clock);
impl_sim_value_cmp_as_bool!(Reset);
impl_sim_value_cmp_as_bool!(SyncReset);
impl_sim_value_cmp_as_bool!(AsyncReset);
#[doc(hidden)]
pub mod match_sim_value {
use crate::{
sim::value::{SimValue, ToSimValue},
ty::Type,
};
#[doc(hidden)]
pub struct MatchSimValueHelper<T>(Option<T>);
impl<T> MatchSimValueHelper<T> {
pub fn new(v: T) -> Self {
Self(Some(v))
}
}
#[doc(hidden)]
pub trait MatchSimValue {
type MatchValue;
/// use `self` so it comes first in the method resolution order
fn __fayalite_match_sim_value(self) -> Self::MatchValue
where
Self: Sized;
}
impl<T: Type> MatchSimValue for MatchSimValueHelper<SimValue<T>> {
type MatchValue = T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::into_value(self.0.expect("should be Some"))
}
}
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a SimValue<T>> {
type MatchValue = &'a T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::value(self.0.expect("should be Some"))
}
}
impl<'a, T: Type> MatchSimValue for MatchSimValueHelper<&'a mut SimValue<T>> {
type MatchValue = &'a mut T::SimValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
SimValue::value_mut(self.0.expect("should be Some"))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ &'a T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'_ mut &'a T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| *v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a &'_ mut T>
where
MatchSimValueHelper<&'a T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &**v)))
}
}
impl<'a, T> MatchSimValue for MatchSimValueHelper<&'a mut &'_ mut T>
where
MatchSimValueHelper<&'a mut T>: MatchSimValue,
{
type MatchValue = <MatchSimValueHelper<&'a mut T> as MatchSimValue>::MatchValue;
fn __fayalite_match_sim_value(self) -> Self::MatchValue {
MatchSimValue::__fayalite_match_sim_value(MatchSimValueHelper(self.0.map(|v| &mut **v)))
}
}
#[doc(hidden)]
pub trait MatchSimValueFallback {
type MatchValue;
/// use `&mut self` so it comes later in the method resolution order than MatchSimValue
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue;
}
impl<T: ToSimValue> MatchSimValueFallback for MatchSimValueHelper<T> {
type MatchValue = <T::Type as Type>::SimValue;
fn __fayalite_match_sim_value(&mut self) -> Self::MatchValue {
SimValue::into_value(self.0.take().expect("should be Some").into_sim_value())
}
}
}
impl SimValuePartialEq for Clock {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
impl SimValuePartialEq for Reset {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
impl SimValuePartialEq for SyncReset {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
impl SimValuePartialEq for AsyncReset {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
}
}
pub trait ToSimValue: ToSimValueWithType<<Self as ToSimValue>::Type> {
type Type: Type;
pub trait ToSimValue: ToSimValueWithType<<Self as ValueType>::Type> + ValueType {
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type>;
#[track_caller]
@ -462,31 +702,31 @@ pub trait ToSimValueWithType<T: Type> {
macro_rules! forward_to_sim_value_with_type {
([$($generics:tt)*] $ty:ty) => {
impl<$($generics)*> ToSimValueWithType<<Self as ToSimValue>::Type> for $ty {
fn to_sim_value_with_type(&self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
impl<$($generics)*> ToSimValueWithType<<Self as ValueType>::Type> for $ty {
fn to_sim_value_with_type(&self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
let retval = Self::to_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
assert_eq!(retval.ty(), ty);
retval
}
#[track_caller]
fn into_sim_value_with_type(self, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type>
fn into_sim_value_with_type(self, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type>
where
Self: Sized,
{
let retval = Self::into_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
assert_eq!(retval.ty(), ty);
retval
}
#[track_caller]
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
fn arc_into_sim_value_with_type(self: Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
let retval = Self::arc_into_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
assert_eq!(retval.ty(), ty);
retval
}
#[track_caller]
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ToSimValue>::Type) -> SimValue<<Self as ToSimValue>::Type> {
fn arc_to_sim_value_with_type(self: &Arc<Self>, ty: <Self as ValueType>::Type) -> SimValue<<Self as ValueType>::Type> {
let retval = Self::arc_to_sim_value(self);
assert_eq!(SimValue::ty(&retval), ty);
assert_eq!(retval.ty(), ty);
retval
}
}
@ -494,7 +734,6 @@ macro_rules! forward_to_sim_value_with_type {
}
impl<T: Type> ToSimValue for SimValue<T> {
type Type = T;
fn to_sim_value(&self) -> SimValue<Self::Type> {
self.clone()
}
@ -550,9 +789,7 @@ impl<T: Type> ToSimValueWithType<T> for BitSlice {
}
}
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ This {
type Type = This::Type;
impl<'a, This: ?Sized + ToSimValue> ToSimValue for &'a This {
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
@ -565,8 +802,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
}
impl<This: ?Sized + ToSimValue> ToSimValue for &'_ mut This {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
@ -579,8 +814,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for &'
}
impl<This: ?Sized + ToSimValue> ToSimValue for Arc<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::arc_to_sim_value(self)
}
@ -601,7 +834,6 @@ impl<This: ?Sized + ToSimValueWithType<T>, T: Type> ToSimValueWithType<T> for Ar
impl<This: ?Sized + ToSimValue + Send + Sync + 'static> ToSimValue
for crate::intern::Interned<This>
{
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
@ -616,8 +848,6 @@ impl<This: ?Sized + ToSimValueWithType<T> + Send + Sync + 'static, T: Type> ToSi
}
impl<This: ToSimValue> ToSimValue for Box<This> {
type Type = This::Type;
fn to_sim_value(&self) -> SimValue<Self::Type> {
This::to_sim_value(self)
}
@ -660,8 +890,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for [
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for [Element] {
type Type = Array<Element::Type>;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
@ -697,8 +925,6 @@ impl<Element: ToSimValue<Type: StaticType>, const N: usize> ToSimValue for [Elem
where
ConstUsize<N>: KnownSize,
{
type Type = Array<Element::Type, N>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(StaticType::TYPE, self)
}
@ -752,8 +978,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for V
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Vec<Element> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
@ -794,8 +1018,6 @@ impl<Element: ToSimValueWithType<T>, T: Type> ToSimValueWithType<Array<T>> for B
}
impl<Element: ToSimValue<Type: StaticType>> ToSimValue for Box<[Element]> {
type Type = Array<Element::Type>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_array_elements(ArrayType::new_dyn(StaticType::TYPE, self.len()), self)
}
@ -825,11 +1047,10 @@ impl<Element: ToSimValueWithType<CanonicalType>> ToSimValueWithType<CanonicalTyp
}
impl<T: Type> ToSimValue for Expr<T> {
type Type = T;
#[track_caller]
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_bitslice(
Expr::ty(*self),
self.ty(),
&crate::expr::ToLiteralBits::to_literal_bits(self)
.expect("must be a literal expression"),
)
@ -849,8 +1070,6 @@ macro_rules! impl_to_sim_value_for_bool_like {
}
impl ToSimValue for bool {
type Type = Bool;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(Bool, *self)
}
@ -888,10 +1107,8 @@ impl ToSimValueWithType<CanonicalType> for bool {
}
macro_rules! impl_to_sim_value_for_primitive_int {
($prim:ident) => {
($prim:ty) => {
impl ToSimValue for $prim {
type Type = <$prim as ToExpr>::Type;
#[track_caller]
fn to_sim_value(
&self,
@ -902,15 +1119,15 @@ macro_rules! impl_to_sim_value_for_primitive_int {
forward_to_sim_value_with_type!([] $prim);
impl ToSimValueWithType<<<$prim as ToExpr>::Type as IntType>::Dyn> for $prim {
impl ToSimValueWithType<<<$prim as ValueType>::Type as IntType>::Dyn> for $prim {
#[track_caller]
fn to_sim_value_with_type(
&self,
ty: <<$prim as ToExpr>::Type as IntType>::Dyn,
) -> SimValue<<<$prim as ToExpr>::Type as IntType>::Dyn> {
ty: <<$prim as ValueType>::Type as IntType>::Dyn,
) -> SimValue<<<$prim as ValueType>::Type as IntType>::Dyn> {
SimValue::from_value(
ty,
<<$prim as ToExpr>::Type as Type>::SimValue::from(*self).as_dyn_int(),
<<$prim as ValueType>::Type as Type>::SimValue::from(*self).as_dyn_int(),
)
}
}
@ -918,7 +1135,7 @@ macro_rules! impl_to_sim_value_for_primitive_int {
impl ToSimValueWithType<CanonicalType> for $prim {
#[track_caller]
fn to_sim_value_with_type(&self, ty: CanonicalType) -> SimValue<CanonicalType> {
let ty: <<$prim as ToExpr>::Type as IntType>::Dyn = Type::from_canonical(ty);
let ty: <<$prim as ValueType>::Type as IntType>::Dyn = Type::from_canonical(ty);
SimValue::into_canonical(self.to_sim_value_with_type(ty))
}
}
@ -937,12 +1154,22 @@ impl_to_sim_value_for_primitive_int!(i32);
impl_to_sim_value_for_primitive_int!(i64);
impl_to_sim_value_for_primitive_int!(i128);
impl_to_sim_value_for_primitive_int!(isize);
impl_to_sim_value_for_primitive_int!(NonZero<u8>);
impl_to_sim_value_for_primitive_int!(NonZero<u16>);
impl_to_sim_value_for_primitive_int!(NonZero<u32>);
impl_to_sim_value_for_primitive_int!(NonZero<u64>);
impl_to_sim_value_for_primitive_int!(NonZero<u128>);
impl_to_sim_value_for_primitive_int!(NonZero<usize>);
impl_to_sim_value_for_primitive_int!(NonZero<i8>);
impl_to_sim_value_for_primitive_int!(NonZero<i16>);
impl_to_sim_value_for_primitive_int!(NonZero<i32>);
impl_to_sim_value_for_primitive_int!(NonZero<i64>);
impl_to_sim_value_for_primitive_int!(NonZero<i128>);
impl_to_sim_value_for_primitive_int!(NonZero<isize>);
macro_rules! impl_to_sim_value_for_int_value {
($IntValue:ident, $Int:ident, $IntType:ident) => {
impl<Width: Size> ToSimValue for $IntValue<Width> {
type Type = $IntType<Width>;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone())
}
@ -1303,8 +1530,6 @@ impl<T: SimOnlyValueTrait> ToSimValueWithType<SimOnly<T>> for SimOnlyValue<T> {
}
impl ToSimValue for DynSimOnlyValue {
type Type = DynSimOnly;
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(self.ty(), self.clone())
}
@ -1314,29 +1539,58 @@ impl ToSimValue for DynSimOnlyValue {
}
}
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
impl<T: SimOnlyValueTrait> ValueType for SimOnlyValue<T> {
type Type = SimOnly<T>;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
SimOnly::new()
}
}
impl<T: SimOnlyValueTrait> ToSimValue for SimOnlyValue<T> {
fn to_sim_value(&self) -> SimValue<Self::Type> {
SimValue::from_value(Default::default(), self.clone())
SimValue::from_value(self.ty(), self.clone())
}
fn into_sim_value(self) -> SimValue<Self::Type> {
SimValue::from_value(Default::default(), self)
SimValue::from_value(self.ty(), self)
}
}
impl SimValuePartialEq for DynSimOnly {
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<Self>) -> bool {
**this == **other
impl HdlPartialEqImpl<Self> for DynSimOnly {
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: Self,
rhs_value: Cow<'_, Self::SimValue>,
) -> bool {
*lhs_value == *rhs_value
}
#[track_caller]
fn cmp_expr_eq(_lhs: Expr<Self>, _rhs: Expr<Self>) -> Expr<Bool> {
panic!("can't compare Expr<DynSimOnly>");
}
}
impl<T: SimOnlyValueTrait + PartialEq<U>, U: SimOnlyValueTrait> SimValuePartialEq<SimOnly<U>>
for SimOnly<T>
impl<L: SimOnlyValueTrait + PartialEq<R>, R: SimOnlyValueTrait> HdlPartialEqImpl<SimOnly<R>>
for SimOnly<L>
{
fn sim_value_eq(this: &SimValue<Self>, other: &SimValue<SimOnly<U>>) -> bool {
***this == ***other
#[track_caller]
fn cmp_value_eq(
_lhs: Self,
lhs_value: Cow<'_, Self::SimValue>,
_rhs: SimOnly<R>,
rhs_value: Cow<'_, <SimOnly<R> as Type>::SimValue>,
) -> bool {
**lhs_value == **rhs_value
}
#[track_caller]
fn cmp_expr_eq(_lhs: Expr<Self>, _rhs: Expr<SimOnly<R>>) -> Expr<Bool> {
panic!("can't compare Expr<SimOnly<_>>");
}
}

View file

@ -3,6 +3,7 @@
//! `unsafe` parts of [`DynSimOnlyValue`]
use crate::expr::{ValueType, value_category::ValueCategoryValue};
use serde::{Serialize, de::DeserializeOwned};
use std::{
any::{self, TypeId},
@ -295,10 +296,16 @@ impl<T: SimOnlyValueTrait> From<SimOnlyValue<T>> for DynSimOnlyValue {
}
}
impl DynSimOnlyValue {
pub fn ty(&self) -> DynSimOnly {
impl ValueType for DynSimOnlyValue {
type Type = DynSimOnly;
type ValueCategory = ValueCategoryValue;
fn ty(&self) -> Self::Type {
self.0.ty()
}
}
impl DynSimOnlyValue {
pub fn type_id(&self) -> TypeId {
self.0.type_id_dyn()
}

View file

@ -11,7 +11,7 @@ use crate::{
intern::{Intern, Interned},
phantom_const::PhantomConst,
reset::{AsyncReset, Reset, SyncReset},
sim::value::{DynSimOnlyValue, DynSimOnly, SimValue, ToSimValueWithType},
sim::value::{DynSimOnly, DynSimOnlyValue, SimValue, ToSimValueWithType},
source_location::SourceLocation,
util::{ConstUsize, slice_range, try_slice_range},
};

View file

@ -25,7 +25,7 @@ impl<T: Type> ReadyValid<T> {
#[hdl]
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
let expr = expr.to_expr();
let option_ty = Expr::ty(expr).data;
let option_ty = expr.ty().data;
#[hdl]
let firing_data = wire(option_ty);
connect(firing_data, option_ty.HdlNone());
@ -42,7 +42,7 @@ impl<T: Type> ReadyValid<T> {
) -> Expr<ReadyValid<R>> {
let data = HdlOption::map(expr.data, f);
#[hdl]
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
let mapped = wire(ReadyValid[data.ty().HdlSome]);
connect(mapped.data, data);
connect(expr.ready, mapped.ready);
mapped
@ -81,7 +81,7 @@ pub fn queue<T: Type>(
let count: UInt = m.output(count_ty);
#[hdl]
let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
let inp_index_reg: UInt = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl]
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
#[hdl]

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information
use crate::{
expr::{Expr, Flow, ToExpr},
expr::{Expr, Flow, ToExpr, ValueType, value_category::ValueCategoryExpr},
intern::Interned,
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
source_location::SourceLocation,
@ -16,7 +16,16 @@ pub struct Wire<T: Type> {
ty: T,
}
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
impl<T: Type> ValueType for Wire<T> {
type Type = T;
type ValueCategory = ValueCategoryExpr;
fn ty(&self) -> Self::Type {
self.ty
}
}
impl<T: Type> fmt::Debug for Wire<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Wire({:?}: ", self.name)?;
self.ty.fmt(f)?;
@ -49,9 +58,6 @@ impl<T: Type> Wire<T> {
ty: T::from_canonical(ty),
}
}
pub fn ty(&self) -> T {
self.ty
}
pub fn new_unchecked(
scoped_name: ScopedNameId,
source_location: SourceLocation,

View file

@ -215,7 +215,7 @@ where
let o: Array<T, N> = m.output();
let bytes = v.to_string().as_bytes().to_expr();
#[hdl]
let o2: Array<UInt<8>> = m.output(Expr::ty(bytes));
let o2: Array<UInt<8>> = m.output(bytes.ty());
connect(
o,
#[hdl]
@ -1176,7 +1176,7 @@ pub fn check_memory_init() {
let waddr2: UInt<4> = m.input();
#[hdl]
let wdata2: UInt<31> = m.input();
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static()));
let mem_init2 = Vec::from_iter((0..0x10u32).map(|i| (i * i * i).cast_to_static::<UInt<31>>()));
#[hdl]
let mut mem2 = memory_with_init(mem_init2);
let read_port2 = mem2.new_read_port();
@ -1784,7 +1784,7 @@ pub fn check_memory_of_bundle_of_arrays() {
[(i * i).wrapping_mul(2), (i * i).wrapping_mul(3)],
[(i * i).wrapping_mul(i), i * 2],
],
i.cast_to_static(),
i.cast_to_static::<UInt<2>>(),
)
}));
#[hdl]
@ -1954,9 +1954,9 @@ pub fn check_memory_of_array_of_bundle() {
let clk: Clock = m.input();
let mem_init = Vec::from_iter((0..0x10u8).map(|i| {
[
(i, i.cast_to_static()),
((i * i), (i / 2).cast_to_static()),
((i * i).wrapping_mul(i), (i / 4).cast_to_static()),
(i, i.cast_to_static::<SInt<1>>()),
((i * i), (i / 2).cast_to_static::<SInt<1>>()),
((i * i).wrapping_mul(i), (i / 4).cast_to_static::<SInt<1>>()),
]
}));
#[hdl]
@ -2269,7 +2269,8 @@ pub fn check_memory_of_bundle() {
let wmask: (Bool, Bool) = m.input();
#[hdl]
let clk: Clock = m.input();
let mem_init = Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static())));
let mem_init =
Vec::from_iter((0..0x10u8).map(|i| (i ^ 3, (i ^ (i / 2)).cast_to_static::<SInt<1>>())));
#[hdl]
let mut mem = memory_with_init(mem_init);
let read_port = mem.new_read_port();

View file

@ -86,7 +86,7 @@ pub fn mod1() {
#[hdl]
let child = instance(mod1_child());
#[hdl]
let o: mod1_child = m.output(Expr::ty(child));
let o: mod1_child = m.output(child.ty());
connect(o, child);
}
@ -506,10 +506,10 @@ fn test_enums() {
data_out: _,
b_out: _,
b2_out: _,
} = expected;
sim.write(sim.io().en, &en);
sim.write(sim.io().which_in, &which_in);
sim.write(sim.io().data_in, &data_in);
} = &expected;
sim.write(sim.io().en, en);
sim.write(sim.io().which_in, which_in);
sim.write(sim.io().data_in, data_in);
let io = #[hdl(sim)]
IO::<_> {
en,
@ -528,7 +528,7 @@ fn test_enums() {
);
// make sure matching on SimValue<SomeEnum> works
#[hdl(sim)]
match io.b_out {
match &io.b_out {
HdlNone => println!("io.b_out is HdlNone"),
HdlSome(v) => println!("io.b_out is HdlSome(({:?}, {:?}))", *v.0, *v.1),
}
@ -706,13 +706,13 @@ fn test_memories() {
w_en,
w_data,
w_mask,
} = expected;
sim.write(sim.io().r.addr, &r_addr);
sim.write(sim.io().r.en, &r_en);
sim.write(sim.io().w.addr, &w_addr);
sim.write(sim.io().w.en, &w_en);
sim.write(sim.io().w.data, &w_data);
sim.write(sim.io().w.mask, &w_mask);
} = &expected;
sim.write(sim.io().r.addr, r_addr);
sim.write(sim.io().r.en, r_en);
sim.write(sim.io().w.addr, w_addr);
sim.write(sim.io().w.en, w_en);
sim.write(sim.io().w.data, w_data);
sim.write(sim.io().w.mask, w_mask);
let io = #[hdl(sim)]
IO {
r_addr,
@ -979,10 +979,10 @@ fn test_memories2() {
},
) in io_cycles.into_iter().enumerate()
{
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static());
sim.write_bool_or_int(sim.io().rw.addr, addr.cast_to_static::<UInt<3>>());
sim.write_bool(sim.io().rw.en, en);
sim.write_bool(sim.io().rw.wmode, wmode);
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static());
sim.write_bool_or_int(sim.io().rw.wdata, wdata.cast_to_static::<UInt<2>>());
sim.write_bool(sim.io().rw.wmask, wmask);
sim.advance_time(SimDuration::from_nanos(250));
sim.write_clock(sim.io().rw.clk, true);
@ -1195,9 +1195,9 @@ fn test_memories3() {
w_data: [0; 8],
w_mask: [false; 8],
});
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static());
sim.write_bool_or_int(sim.io().r.addr, r_addr.cast_to_static::<UInt<3>>());
sim.write_bool(sim.io().r.en, r_en);
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static());
sim.write_bool_or_int(sim.io().w.addr, w_addr.cast_to_static::<UInt<3>>());
sim.write_bool(sim.io().w.en, w_en);
for (i, v) in w_data.into_iter().enumerate() {
sim.write_bool_or_int(sim.io().w.data[i], v);
@ -1505,7 +1505,7 @@ fn test_many_memories() {
w_en,
w_data,
w_mask,
} = expected;
} = &expected;
for (((r, w), w_data), w_mask) in sim
.io()
.r
@ -1514,10 +1514,10 @@ fn test_many_memories() {
.zip(w_data.iter())
.zip(w_mask.iter())
{
sim.write(r.addr, &r_addr);
sim.write(r.en, &r_en);
sim.write(w.addr, &w_addr);
sim.write(w.en, &w_en);
sim.write(r.addr, r_addr);
sim.write(r.en, r_en);
sim.write(w.addr, w_addr);
sim.write(w.en, w_en);
sim.write(w.data, w_data);
sim.write(w.mask, w_mask);
}

View file

@ -21,7 +21,7 @@ Simulation {
},
SlotDebugData {
name: "",
ty: Bool,
ty: UInt<1>,
},
SlotDebugData {
name: "",
@ -51,12 +51,12 @@ Simulation {
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
dest: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
value: 0x1,
},
1: Copy {
dest: StatePartIndex<BigSlots>(3), // (0x1) SlotDebugData { name: "", ty: AsyncReset },
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(2), // (0x1) SlotDebugData { name: "", ty: UInt<1> },
},
// at: module-XXXXXXXXXX.rs:4:1
2: Copy {

View file

@ -30,7 +30,7 @@ Simulation {
..
},
big_slots: StatePartLayout<BigSlots> {
len: 7,
len: 8,
debug_data: [
SlotDebugData {
name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr",
@ -54,12 +54,16 @@ Simulation {
},
SlotDebugData {
name: "",
ty: Bool,
ty: UInt<1>,
},
SlotDebugData {
name: "",
ty: Clock,
},
SlotDebugData {
name: "",
ty: Bool,
},
],
..
},
@ -90,78 +94,83 @@ Simulation {
insns: [
// at: module-XXXXXXXXXX.rs:1:1
0: Const {
dest: StatePartIndex<BigSlots>(5), // (0x0) SlotDebugData { name: "", ty: Bool },
dest: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "", ty: Bool },
value: 0x0,
},
// at: module-XXXXXXXXXX.rs:7:1
1: Copy {
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool },
src: StatePartIndex<BigSlots>(7), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:1:1
2: Const {
dest: StatePartIndex<BigSlots>(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
value: 0x0,
},
3: Copy {
dest: StatePartIndex<BigSlots>(6), // (0x0) SlotDebugData { name: "", ty: Clock },
src: StatePartIndex<BigSlots>(5), // (0x0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(5), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
// at: module-XXXXXXXXXX.rs:6:1
2: Copy {
4: Copy {
dest: StatePartIndex<BigSlots>(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock },
src: StatePartIndex<BigSlots>(6), // (0x0) SlotDebugData { name: "", ty: Clock },
},
// at: module-XXXXXXXXXX.rs:7:1
3: Copy {
dest: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool },
src: StatePartIndex<BigSlots>(5), // (0x0) SlotDebugData { name: "", ty: Bool },
},
// at: module-XXXXXXXXXX.rs:1:1
4: Const {
5: Const {
dest: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> },
value: 0x0,
},
5: CastToUInt {
6: CastToUInt {
dest: StatePartIndex<BigSlots>(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> },
src: StatePartIndex<BigSlots>(3), // (0x0) SlotDebugData { name: "", ty: UInt<8> },
dest_width: 0,
},
// at: module-XXXXXXXXXX.rs:5:1
6: Copy {
7: Copy {
dest: StatePartIndex<BigSlots>(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> },
src: StatePartIndex<BigSlots>(4), // (0x0) SlotDebugData { name: "", ty: UInt<0> },
},
// at: module-XXXXXXXXXX.rs:3:1
7: CastBigToArrayIndex {
8: CastBigToArrayIndex {
dest: StatePartIndex<SmallSlots>(4), // (0x0 0) SlotDebugData { name: "", ty: UInt<0> },
src: StatePartIndex<BigSlots>(0), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.addr", ty: UInt<0> },
},
8: IsNonZeroDestIsSmall {
9: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(1), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.en", ty: Bool },
},
9: BranchIfSmallZero {
target: 11,
10: BranchIfSmallZero {
target: 12,
value: StatePartIndex<SmallSlots>(3), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
10: Branch {
target: 11,
11: Branch {
target: 12,
},
11: IsNonZeroDestIsSmall {
12: IsNonZeroDestIsSmall {
dest: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(2), // (0x0) SlotDebugData { name: "InstantiatedModule(phantom_const: phantom_const).phantom_const::mem::r0.clk", ty: Clock },
},
12: AndSmall {
13: AndSmall {
dest: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
rhs: StatePartIndex<SmallSlots>(0), // (0x1 1) SlotDebugData { name: "", ty: Bool },
},
13: BranchIfSmallZero {
target: 14,
14: BranchIfSmallZero {
target: 15,
value: StatePartIndex<SmallSlots>(1), // (0x0 0) SlotDebugData { name: "", ty: Bool },
},
14: XorSmallImmediate {
15: XorSmallImmediate {
dest: StatePartIndex<SmallSlots>(0), // (0x1 1) SlotDebugData { name: "", ty: Bool },
lhs: StatePartIndex<SmallSlots>(2), // (0x0 0) SlotDebugData { name: "", ty: Bool },
rhs: 0x1,
},
// at: module-XXXXXXXXXX.rs:1:1
15: Return,
16: Return,
],
..
},
pc: 15,
pc: 16,
memory_write_log: [],
memories: StatePart {
value: [
@ -192,6 +201,7 @@ Simulation {
0,
0,
0,
0,
],
},
sim_only_slots: StatePart {

View file

@ -162,7 +162,7 @@ Simulation {
},
SlotDebugData {
name: "",
ty: Bool,
ty: UInt<1>,
},
SlotDebugData {
name: "",
@ -429,12 +429,12 @@ Simulation {
},
// at: module-XXXXXXXXXX.rs:1:1
26: Const {
dest: StatePartIndex<BigSlots>(28), // (0x0) SlotDebugData { name: "", ty: Bool },
dest: StatePartIndex<BigSlots>(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
value: 0x0,
},
27: Copy {
dest: StatePartIndex<BigSlots>(29), // (0x0) SlotDebugData { name: "", ty: SyncReset },
src: StatePartIndex<BigSlots>(28), // (0x0) SlotDebugData { name: "", ty: Bool },
src: StatePartIndex<BigSlots>(28), // (0x0) SlotDebugData { name: "", ty: UInt<1> },
},
28: Copy {
dest: StatePartIndex<BigSlots>(26), // (0x1) SlotDebugData { name: ".clk", ty: Clock },

View file

@ -4,14 +4,14 @@ error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between thr
11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
| ^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -28,13 +28,13 @@ error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between th
11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
| ^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -51,7 +51,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta
11 | fn f(v: SimValue<()>) -> Interned<SimValue<()>> {
| ^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely
|
= help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
note: required because it appears within the type `DynSimOnlyValue`
--> src/sim/value/sim_only_value_unsafe.rs
|
@ -92,7 +92,7 @@ note: required because it appears within the type `util::alternating_cell::Alter
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -103,29 +103,15 @@ note: required by a bound in `fayalite::intern::Interned`
| pub struct Interned<T: ?Sized + 'static + Send + Sync> {
| ^^^^ required by this bound in `Interned`
error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied
error[E0277]: the trait bound `fayalite::prelude::SimValue<()>: Intern` is not satisfied
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ the trait `Hash` is not implemented for `SimValue<()>`
| -------------------- ^ the trait `Hash` is not implemented for `fayalite::prelude::SimValue<()>`
| |
| required by a bound introduced by this call
|
= note: required for `SimValue<()>` to implement `Intern`
help: consider dereferencing here
|
12 | Intern::intern_sized(*v)
| +
error[E0277]: the trait bound `SimValue<()>: Intern` is not satisfied
--> tests/ui/simvalue_is_not_internable.rs:12:26
|
12 | Intern::intern_sized(v)
| -------------------- ^ the trait `std::cmp::Eq` is not implemented for `SimValue<()>`
| |
| required by a bound introduced by this call
|
= note: required for `SimValue<()>` to implement `Intern`
= note: required for `fayalite::prelude::SimValue<()>` to implement `Intern`
help: consider dereferencing here
|
12 | Intern::intern_sized(*v)
@ -139,14 +125,14 @@ error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between thr
| |
| required by a bound introduced by this call
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -172,13 +158,13 @@ error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between th
| |
| required by a bound introduced by this call
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -204,7 +190,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta
| |
| required by a bound introduced by this call
|
= help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
note: required because it appears within the type `DynSimOnlyValue`
--> src/sim/value/sim_only_value_unsafe.rs
|
@ -245,7 +231,7 @@ note: required because it appears within the type `util::alternating_cell::Alter
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -269,14 +255,14 @@ error[E0277]: `Cell<util::alternating_cell::State>` cannot be shared between thr
12 | Intern::intern_sized(v)
| ^^^^^^^^^^^^^^^^^^^^^^^ `Cell<util::alternating_cell::State>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `Cell<util::alternating_cell::State>`
= note: if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -293,13 +279,13 @@ error[E0277]: `UnsafeCell<value::SimValueInner<()>>` cannot be shared between th
12 | Intern::intern_sized(v)
| ^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<value::SimValueInner<()>>` cannot be shared between threads safely
|
= help: within `SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Sync` is not implemented for `UnsafeCell<value::SimValueInner<()>>`
note: required because it appears within the type `util::alternating_cell::AlternatingCell<value::SimValueInner<()>>`
--> src/util/alternating_cell.rs
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {
@ -316,7 +302,7 @@ error[E0277]: `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'sta
12 | Intern::intern_sized(v)
| ^^^^^^^^^^^^^^^^^^^^^^^ `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>` cannot be sent between threads safely
|
= help: within `SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
= help: within `fayalite::prelude::SimValue<()>`, the trait `Send` is not implemented for `Rc<(dyn value::sim_only_value_unsafe::DynSimOnlyValueTrait + 'static)>`
note: required because it appears within the type `DynSimOnlyValue`
--> src/sim/value/sim_only_value_unsafe.rs
|
@ -357,7 +343,7 @@ note: required because it appears within the type `util::alternating_cell::Alter
|
| pub(crate) struct AlternatingCell<T: ?Sized> {
| ^^^^^^^^^^^^^^^
note: required because it appears within the type `SimValue<()>`
note: required because it appears within the type `fayalite::prelude::SimValue<()>`
--> src/sim/value.rs
|
| pub struct SimValue<T: Type> {

171
rocq-demo/rocq_hdl.v Normal file
View file

@ -0,0 +1,171 @@
(* SPDX-License-Identifier: LGPL-3.0-or-later
See Notices.txt for copyright information *)
(* This file demonstrates converting simple HDL designs into Rocq, as well
as proofs by induction that parallels those of formal verification in
Symbiyosys with the "smtbmc" engine in the "prove" mode. *)
(* Models a simple register, feeding back to itself. We prove that,
when initialized to zero, it stays always at zero. *)
Module simple_register.
(* State record. Contains only one boolean variable, which we call "a" *)
Record S := { a: bool }.
(* The above automatically defines an accessor function "a: S->bool", so
we can write "a s" to get the value of "a" in state "s". *)
(* Defines an "initial" predicate, so "initial s" means
"s is an initial state". In our case, the value of "a"
in the initial state is zero (false) *)
Definition initial (s: S) :=
a s = false.
(* As an example, we construct a state where "a" is indeed false,
and prove it is an initial state. The syntax for instantiating
such a record is "{| a := false |}". *)
Example test_initial: initial {| a := false |}.
Proof. reflexivity. Qed.
(* Defines an "assert" predicate, so "assert s" means "assertions
hold in state s" or equivalently "state s is valid". In our case,
valid states correspond to those where "a" is false. *)
Definition assert (s: S) :=
a s = false.
(* The following proof by induction follows the article "Temporal
Induction by Incremental SAT Solving",
https://doi.org/10.1016/S1571-0661(05)82542-3 *)
(* Proves the base case, with one step: the initial state is a
valid state. *)
Theorem base1: forall s0, initial s0 -> assert s0.
Proof. trivial. Qed.
(* Defines a "transition" predicate, so "t s1 s2" means:
"s2 can be reached in one step from s1". In our case,
the value of "a" in s2 is the same as in s1. *)
Definition t (s1 s2 : S) :=
a s2 = a s1.
(* Induction principle: for all transitions from a valid state,
we reach a valid state. *)
Theorem induct1:
forall s0 s1, assert s0 -> t s0 s1 -> assert s1.
Proof.
unfold t, assert.
intros s0 s1 A T.
transitivity (a s0).
apply T. apply A.
Qed.
(* Here we have proven the base case and the inductive case, so we
declare success. However, we may want to formalize it from first
principles, something like "All reacheable states from an initial
state are valid", and use the above theorems to prove it (TODO). *)
(* Example: all states reacheable in one step from the initial state
are valid. *)
Example one_step_valid:
forall s0 s1, initial s0 -> t s0 s1 -> assert s1.
Proof.
unfold initial, t, assert.
intros s0 s1 I T.
transitivity (a s0).
apply T. apply I.
Qed.
(* Same as above, but we prove using only the induction theorems. *)
Example one_step_valid':
forall s0 s1, initial s0 -> t s0 s1 -> assert s1.
Proof.
apply induct1.
Qed.
End simple_register.
(* The same circuit as above, but with an additional output register.
The output is asserted to be always false, but no such restriction
is made on the inner register, a "hidden" state. It creates a
difficulty for induction: there are unreacheable valid states that
leads to invalid states. A two step induction solves the problem. *)
Module hidden_state.
(* Demonstrate state with several (two) variables. *)
Record S :=
{ a: bool
; b: bool
}.
(* Both registers start at zero (false). *)
Definition initial (s: S) :=
a s = false /\ b s = false.
(* Just b is constrained to be zero (false) *)
Definition assert (s: S) :=
b s = false.
(* "a" keeps its previous value, "b" copies "a". *)
Definition t (s1 s2 : S) :=
a s2 = a s1 /\ b s2 = a s1.
(* Base case in one step. *)
Theorem base1:
forall s0,
initial s0 -> assert s0.
Proof.
unfold initial, assert.
intros s0 [_ H].
apply H.
Qed.
(* Try proving the inductive case in one step. *)
Theorem induct1:
forall s0 s1,
assert s0 -> t s0 s1 -> assert s1.
Proof.
unfold t, assert.
intros s0 s1 A T.
destruct T as [_ Tb].
transitivity (a s0). apply Tb.
Abort.
(* Ops, we reach a dead-end. Maybe it's not provable after all?
Try proving its negation by finding a counter-example. *)
Theorem not_induct1:
not (forall s0 s1,
assert s0 -> t s0 s1 -> assert s1).
Proof.
unfold not, assert, t.
intros H.
specialize H with (s0 := {|a := true; b := false|}).
simpl in H.
specialize H with (s1 := {|a := true; b := true|}).
simpl in H.
discriminate H. reflexivity. auto.
Qed.
(* Indeed, a valid state where "a" is one (true) leads to
an invalid state. Try one more step. *)
Theorem base2:
forall s0 s1,
initial s0 -> assert s0 -> t s0 s1 -> assert s1.
Proof.
intros s0 s1 [Ia _] _ [_ Tb].
unfold assert.
transitivity (a s0). apply Tb.
apply Ia.
Qed.
Theorem induct2:
forall s0 s1 s2,
assert s0 -> t s0 s1 -> assert s1 -> t s1 s2 -> assert s2.
Proof.
intros s0 s1 s2 A0 [T0a T0b] A1 [T1a T1b].
unfold assert in *.
transitivity (a s1). apply T1b.
transitivity (a s0). apply T0a.
transitivity (b s1). symmetry. apply T0b.
apply A1.
Qed.
(* Success! Two steps indeed work. *)
(* Example: all states reacheable in two steps are valid *)
Example two_steps_valid:
forall s0 s1 s2,
initial s0 -> t s0 s1 -> t s1 s2 -> assert s2.
Proof.
intros s0 s1 s2.
intros Hi T01.
apply induct2 with (s0 := s0).
- apply base1. apply Hi.
- apply T01.
- apply base2 with (s0 := s0).
apply Hi. apply base1. apply Hi. apply T01.
Qed.
End hidden_state.

View file

@ -32,6 +32,7 @@ POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notice
SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$')
MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$')
JSON_HEADER=('^"{"$' '^" \"license_header\": ["$' '^" \"SPDX-License-Identifier: LGPL-3.0-or-later\","$' '^" \"See Notices.txt for copyright information\""')
ROCQ_HEADER=('^"(* SPDX-License-Identifier: LGPL-3.0-or-later"$' '^" See Notices.txt for copyright information *)"$')
function main()
{
@ -64,6 +65,9 @@ function main()
*.json)
check_file "$file" "${JSON_HEADER[@]}"
;;
*.v)
check_file "$file" "${ROCQ_HEADER[@]}"
;;
*)
fail_file "$file" 0 "unimplemented file kind -- you need to add it to $0"
;;