Compare commits

...

3 commits

9 changed files with 471 additions and 38 deletions

View file

@ -83,6 +83,7 @@ impl ParsedBundle {
custom_bounds,
no_static: _,
no_runtime_generics: _,
cmp_eq: _,
} = options.body;
let mut fields = match fields {
syn::Fields::Named(fields) => fields,
@ -437,6 +438,7 @@ impl ToTokens for ParsedBundle {
custom_bounds: _,
no_static,
no_runtime_generics,
cmp_eq,
} = &options.body;
let target = get_target(target, ident);
let mut item_attrs = attrs.clone();
@ -765,6 +767,69 @@ impl ToTokens for ParsedBundle {
}
}
.to_tokens(tokens);
if let Some((cmp_eq,)) = cmp_eq {
let mut where_clause =
Generics::from(generics)
.where_clause
.unwrap_or_else(|| syn::WhereClause {
where_token: Token![where](span),
predicates: Punctuated::new(),
});
let mut fields_cmp_eq = vec![];
let mut fields_cmp_ne = vec![];
for field in fields.named() {
let field_ident = field.ident();
let field_ty = field.ty();
where_clause
.predicates
.push(parse_quote_spanned! {cmp_eq.span=>
#field_ty: ::fayalite::expr::ops::ExprPartialEq<#field_ty>
});
fields_cmp_eq.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_eq(__lhs.#field_ident, __rhs.#field_ident)
});
fields_cmp_ne.push(quote_spanned! {span=>
::fayalite::expr::ops::ExprPartialEq::cmp_ne(__lhs.#field_ident, __rhs.#field_ident)
});
}
let cmp_eq_body;
let cmp_ne_body;
if fields_len == 0 {
cmp_eq_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&true)
};
cmp_ne_body = quote_spanned! {span=>
::fayalite::expr::ToExpr::to_expr(&false)
};
} else {
cmp_eq_body = quote_spanned! {span=>
#(#fields_cmp_eq)&*
};
cmp_ne_body = quote_spanned! {span=>
#(#fields_cmp_ne)|*
};
};
quote_spanned! {span=>
#[automatically_derived]
impl #impl_generics ::fayalite::expr::ops::ExprPartialEq<Self> for #target #type_generics
#where_clause
{
fn cmp_eq(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_eq_body
}
fn cmp_ne(
__lhs: ::fayalite::expr::Expr<Self>,
__rhs: ::fayalite::expr::Expr<Self>,
) -> ::fayalite::expr::Expr<::fayalite::int::Bool> {
#cmp_ne_body
}
}
}
.to_tokens(tokens);
}
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
let static_generics = generics.clone().for_static_type();
let (static_impl_generics, static_type_generics, static_where_clause) =

View file

@ -155,7 +155,11 @@ impl ParsedEnum {
custom_bounds,
no_static: _,
no_runtime_generics: _,
cmp_eq,
} = options.body;
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums");
}
attrs.retain(|attr| {
if attr.path().is_ident("repr") {
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
@ -211,6 +215,7 @@ impl ToTokens for ParsedEnum {
custom_bounds: _,
no_static,
no_runtime_generics,
cmp_eq: _, // TODO: implement cmp_eq for enums
} = &options.body;
let target = get_target(target, ident);
let mut struct_attrs = attrs.clone();

View file

@ -49,10 +49,14 @@ impl ParsedTypeAlias {
custom_bounds,
no_static,
no_runtime_generics: _,
cmp_eq,
} = options.body;
if let Some((no_static,)) = no_static {
errors.error(no_static, "no_static is not valid on type aliases");
}
if let Some((cmp_eq,)) = cmp_eq {
errors.error(cmp_eq, "cmp_eq is not valid on type aliases");
}
let generics = if custom_bounds.is_some() {
MaybeParsed::Unrecognized(generics)
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
@ -95,6 +99,7 @@ impl ToTokens for ParsedTypeAlias {
custom_bounds: _,
no_static: _,
no_runtime_generics,
cmp_eq: _,
} = &options.body;
let target = get_target(target, ident);
let mut type_attrs = attrs.clone();

View file

@ -26,6 +26,7 @@ crate::options! {
CustomBounds(custom_bounds),
NoStatic(no_static),
NoRuntimeGenerics(no_runtime_generics),
CmpEq(cmp_eq),
}
}
@ -2069,11 +2070,16 @@ macro_rules! impl_bounds {
$(
$Variant:ident,
)*
$(
#[unknown]
$Unknown:ident,
)?
}
) => {
#[derive(Clone, Debug)]
$vis enum $enum_type {
$($Variant(known_items::$Variant),)*
$($Unknown(syn::TypeParamBound),)?
}
$(impl From<known_items::$Variant> for $enum_type {
@ -2086,28 +2092,54 @@ macro_rules! impl_bounds {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
$(Self::$Variant(v) => v.to_tokens(tokens),)*
$(Self::$Unknown(v) => v.to_tokens(tokens),)?
}
}
}
impl $enum_type {
$vis fn parse_path(path: Path) -> Result<Self, Path> {
#![allow(unreachable_code)]
$(let path = match known_items::$Variant::parse_path(path) {
Ok(v) => return Ok(Self::$Variant(v)),
Err(path) => path,
};)*
$(return Ok(Self::$Unknown(syn::TraitBound {
paren_token: None,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path,
}.into()));)?
Err(path)
}
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result<Self, syn::TypeParamBound> {
#![allow(unreachable_code)]
if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound {
if let syn::TraitBound {
paren_token: _,
modifier: syn::TraitBoundModifier::None,
lifetimes: None,
path: _,
} = trait_bound {
match Self::parse_path(trait_bound.path) {
Ok(retval) => return Ok(retval),
Err(path) => trait_bound.path = path,
}
}
type_param_bound = trait_bound.into();
}
$(return Ok(Self::$Unknown(type_param_bound));)?
Err(type_param_bound)
}
}
impl Parse for $enum_type {
fn parse(input: ParseStream) -> syn::Result<Self> {
Self::parse_path(Path::parse_mod_style(input)?).map_err(|path| {
syn::Error::new_spanned(
path,
Self::parse_type_param_bound(input.parse()?)
.map_err(|type_param_bound| syn::Error::new_spanned(
type_param_bound,
format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")),
)
})
))
}
}
@ -2115,6 +2147,7 @@ macro_rules! impl_bounds {
#[allow(non_snake_case)]
$vis struct $struct_type {
$($vis $Variant: Option<known_items::$Variant>,)*
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
}
impl ToTokens for $struct_type {
@ -2126,42 +2159,63 @@ macro_rules! impl_bounds {
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
$(for v in &self.$Unknown {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
}
}
const _: () = {
#[derive(Clone, Debug)]
$vis struct Iter($vis $struct_type);
#[allow(non_snake_case)]
$vis struct Iter {
$($Variant: Option<known_items::$Variant>,)*
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
}
impl IntoIterator for $struct_type {
type Item = $enum_type;
type IntoIter = Iter;
fn into_iter(self) -> Self::IntoIter {
Iter(self)
Iter {
$($Variant: self.$Variant,)*
$($Unknown: self.$Unknown.into_iter(),)?
}
}
}
impl Iterator for Iter {
type Item = $enum_type;
fn next(&mut self) -> Option<Self::Item> {
$(
if let Some(value) = self.0.$Variant.take() {
if let Some(value) = self.$Variant.take() {
return Some($enum_type::$Variant(value));
}
)*
$(
if let Some(value) = self.$Unknown.next() {
return Some($enum_type::$Unknown(value));
}
)?
None
}
#[allow(unused_mut, unused_variables)]
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
$(
if let Some(value) = self.0.$Variant.take() {
if let Some(value) = self.$Variant.take() {
init = f(init, $enum_type::$Variant(value));
}
)*
$(
if let Some(value) = self.$Unknown.next() {
init = f(init, $enum_type::$Unknown(value));
}
)?
init
}
}
@ -2173,6 +2227,9 @@ macro_rules! impl_bounds {
$($enum_type::$Variant(v) => {
self.$Variant = Some(v);
})*
$($enum_type::$Unknown(v) => {
self.$Unknown.push(v);
})?
});
}
}
@ -2191,6 +2248,7 @@ macro_rules! impl_bounds {
$(if let Some(v) = v.$Variant {
self.$Variant = Some(v);
})*
$(self.$Unknown.extend(v.$Unknown);)*
});
}
}
@ -2244,6 +2302,8 @@ impl_bounds! {
Size,
StaticType,
Type,
#[unknown]
Unknown,
}
}
@ -2257,6 +2317,8 @@ impl_bounds! {
ResetType,
StaticType,
Type,
#[unknown]
Unknown,
}
}
@ -2270,6 +2332,7 @@ impl From<ParsedTypeBound> for ParsedBound {
ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v),
ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v),
ParsedTypeBound::Type(v) => ParsedBound::Type(v),
ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v),
}
}
}
@ -2284,6 +2347,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
ResetType,
StaticType,
Type,
Unknown,
} = value;
Self {
BoolOrIntType,
@ -2295,6 +2359,7 @@ impl From<ParsedTypeBounds> for ParsedBounds {
Size: None,
StaticType,
Type,
Unknown,
}
}
}
@ -2330,6 +2395,7 @@ impl ParsedTypeBound {
ParsedTypeBound::Type(known_items::Type(span)),
]),
Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]),
Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]),
}
}
}
@ -2364,6 +2430,7 @@ impl From<ParsedSizeTypeBounds> for ParsedBounds {
Size,
StaticType: None,
Type: None,
Unknown: vec![],
}
}
}
@ -2391,6 +2458,7 @@ impl ParsedBounds {
fn categorize(self, errors: &mut Errors, span: Span) -> ParsedBoundsCategory {
let mut type_bounds = None;
let mut size_type_bounds = None;
let mut unknown_bounds = vec![];
self.into_iter().for_each(|bound| match bound.categorize() {
ParsedBoundCategory::Type(bound) => {
type_bounds
@ -2402,15 +2470,37 @@ impl ParsedBounds {
.get_or_insert_with(ParsedSizeTypeBounds::default)
.extend([bound]);
}
ParsedBoundCategory::Unknown(bound) => unknown_bounds.push(bound),
});
match (type_bounds, size_type_bounds) {
(None, None) => ParsedBoundsCategory::Type(ParsedTypeBounds {
match (type_bounds, size_type_bounds, unknown_bounds.is_empty()) {
(None, None, true) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Type: Some(known_items::Type(span)),
..Default::default()
}),
(None, Some(bounds)) => ParsedBoundsCategory::SizeType(bounds),
(Some(bounds), None) => ParsedBoundsCategory::Type(bounds),
(Some(type_bounds), Some(size_type_bounds)) => {
(None, None, false) => {
errors.error(
unknown_bounds.remove(0),
"unknown bounds: must use at least one known bound (such as `Type`) with any unknown bounds",
);
ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..Default::default()
})
}
(None, Some(bounds), true) => ParsedBoundsCategory::SizeType(bounds),
(None, Some(bounds), false) => {
// TODO: implement
errors.error(
unknown_bounds.remove(0),
"unknown bounds with `Size` bounds are not implemented",
);
ParsedBoundsCategory::SizeType(bounds)
}
(Some(bounds), None, _) => ParsedBoundsCategory::Type(ParsedTypeBounds {
Unknown: unknown_bounds,
..bounds
}),
(Some(type_bounds), Some(size_type_bounds), _) => {
errors.error(
size_type_bounds
.Size
@ -2427,6 +2517,7 @@ impl ParsedBounds {
pub(crate) enum ParsedBoundCategory {
Type(ParsedTypeBound),
SizeType(ParsedSizeTypeBound),
Unknown(syn::TypeParamBound),
}
impl ParsedBound {
@ -2441,12 +2532,14 @@ impl ParsedBound {
Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)),
Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)),
Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)),
Self::Unknown(v) => ParsedBoundCategory::Unknown(v),
}
}
fn implied_bounds(self) -> ParsedBounds {
match self.categorize() {
ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
ParsedBoundCategory::Unknown(v) => ParsedBounds::from_iter([ParsedBound::Unknown(v)]),
}
}
}
@ -3325,7 +3418,7 @@ impl ParsedGenerics {
| ParsedTypeBound::EnumType(_)
| ParsedTypeBound::IntType(_)
| ParsedTypeBound::ResetType(_) => {
errors.error(bound, "bound on mask type not implemented");
errors.error(bound, "bounds on mask types are not implemented");
}
ParsedTypeBound::StaticType(bound) => {
if bounds.StaticType.is_none() {
@ -3337,6 +3430,12 @@ impl ParsedGenerics {
}
}
ParsedTypeBound::Type(_) => {}
ParsedTypeBound::Unknown(_) => {
errors.error(
bound,
"unknown bounds on mask types are not implemented",
);
}
}
}
bounds.add_implied_bounds();

View file

@ -72,13 +72,14 @@ mod kw {
custom_keyword!(cfg);
custom_keyword!(cfg_attr);
custom_keyword!(clock_domain);
custom_keyword!(cmp_eq);
custom_keyword!(connect_inexact);
custom_keyword!(custom_bounds);
custom_keyword!(flip);
custom_keyword!(hdl);
custom_keyword!(hdl_module);
custom_keyword!(input);
custom_keyword!(incomplete_wire);
custom_keyword!(input);
custom_keyword!(instance);
custom_keyword!(m);
custom_keyword!(memory);

View file

@ -2,8 +2,11 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::ArrayIndex, Expr, ToExpr},
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
expr::{
ops::{ArrayIndex, ArrayLiteral, ExprPartialEq},
CastToBits, Expr, HdlPartialEq, ReduceBits, ToExpr,
},
int::{Bool, DynSize, KnownSize, Size, SizeType, DYN_SIZE},
intern::{Intern, Interned, LazyInterned},
module::transform::visit::{Fold, Folder, Visit, Visitor},
source_location::SourceLocation,
@ -218,3 +221,36 @@ impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
}
}
impl<Lhs: Type, Rhs: Type, Len: Size> ExprPartialEq<ArrayType<Rhs, Len>> for ArrayType<Lhs, Len>
where
Lhs: ExprPartialEq<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());
ArrayLiteral::<Bool, DynSize>::new(
Bool,
(0..lhs_ty.len())
.map(|i| Expr::canonical(lhs[i].cmp_eq(rhs[i])))
.collect(),
)
.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());
ArrayLiteral::<Bool, DynSize>::new(
Bool,
(0..lhs_ty.len())
.map(|i| Expr::canonical(lhs[i].cmp_ne(rhs[i])))
.collect(),
)
.cast_to_bits()
.any_one_bits()
}
}

View file

@ -2,7 +2,11 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::BundleLiteral, Expr, ToExpr},
expr::{
ops::{ArrayLiteral, BundleLiteral, ExprPartialEq},
CastToBits, Expr, ReduceBits, ToExpr,
},
int::{Bool, DynSize},
intern::{Intern, Interned},
sim::{SimValue, ToSimValue},
source_location::SourceLocation,
@ -325,7 +329,19 @@ macro_rules! impl_tuple_builder_fields {
}
macro_rules! impl_tuples {
([$({#[num = $num:literal, field = $field:ident, ty = $ty_var:ident: $Ty:ident] $var:ident: $T:ident})*] []) => {
(
[$({
#[
num = $num:tt,
field = $field:ident,
ty = $ty_var:ident: $Ty:ident,
lhs = $lhs_var:ident: $Lhs:ident,
rhs = $rhs_var:ident: $Rhs:ident
]
$var:ident: $T:ident
})*]
[]
) => {
impl_tuple_builder_fields! {
{}
[$({
@ -498,6 +514,29 @@ macro_rules! impl_tuples {
Self::into_sim_value(*self, ty)
}
}
impl<$($Lhs: Type + ExprPartialEq<$Rhs>, $Rhs: Type,)*> ExprPartialEq<($($Rhs,)*)> for ($($Lhs,)*) {
fn cmp_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)),)*]),
)
.cast_to_bits()
.all_one_bits()
}
fn cmp_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)),)*]),
)
.cast_to_bits()
.any_one_bits()
}
}
};
([$($lhs:tt)*] [$rhs_first:tt $($rhs:tt)*]) => {
impl_tuples!([$($lhs)*] []);
@ -507,18 +546,18 @@ macro_rules! impl_tuples {
impl_tuples! {
[] [
{#[num = 0, field = field_0, ty = ty0: Ty0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11] v11: T11}
{#[num = 0, field = field_0, ty = ty0: Ty0, lhs = lhs0: Lhs0, rhs = rhs0: Rhs0] v0: T0}
{#[num = 1, field = field_1, ty = ty1: Ty1, lhs = lhs1: Lhs1, rhs = rhs1: Rhs1] v1: T1}
{#[num = 2, field = field_2, ty = ty2: Ty2, lhs = lhs2: Lhs2, rhs = rhs2: Rhs2] v2: T2}
{#[num = 3, field = field_3, ty = ty3: Ty3, lhs = lhs3: Lhs3, rhs = rhs3: Rhs3] v3: T3}
{#[num = 4, field = field_4, ty = ty4: Ty4, lhs = lhs4: Lhs4, rhs = rhs4: Rhs4] v4: T4}
{#[num = 5, field = field_5, ty = ty5: Ty5, lhs = lhs5: Lhs5, rhs = rhs5: Rhs5] v5: T5}
{#[num = 6, field = field_6, ty = ty6: Ty6, lhs = lhs6: Lhs6, rhs = rhs6: Rhs6] v6: T6}
{#[num = 7, field = field_7, ty = ty7: Ty7, lhs = lhs7: Lhs7, rhs = rhs7: Rhs7] v7: T7}
{#[num = 8, field = field_8, ty = ty8: Ty8, lhs = lhs8: Lhs8, rhs = rhs8: Rhs8] v8: T8}
{#[num = 9, field = field_9, ty = ty9: Ty9, lhs = lhs9: Lhs9, rhs = rhs9: Rhs9] v9: T9}
{#[num = 10, field = field_10, ty = ty10: Ty10, lhs = lhs10: Lhs10, rhs = rhs10: Rhs10] v10: T10}
{#[num = 11, field = field_11, ty = ty11: Ty11, lhs = lhs11: Lhs11, rhs = rhs11: Rhs11] v11: T11}
]
}

View file

@ -2,7 +2,10 @@
// See Notices.txt for copyright information
use crate::{
expr::{ops::VariantAccess, Expr, ToExpr},
expr::{
ops::{ExprPartialEq, VariantAccess},
Expr, ToExpr,
},
hdl,
int::Bool,
intern::{Intern, Interned},
@ -360,6 +363,60 @@ pub enum HdlOption<T: Type> {
HdlSome(T),
}
impl<Lhs: Type + ExprPartialEq<Rhs>, Rhs: Type> ExprPartialEq<HdlOption<Rhs>> for HdlOption<Lhs> {
#[hdl]
fn cmp_eq(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_eq = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_eq, ExprPartialEq::cmp_eq(lhs, rhs)),
HdlNone => connect(cmp_eq, false),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_eq, false),
HdlNone => connect(cmp_eq, true),
}
}
}
cmp_eq
}
#[hdl]
fn cmp_ne(lhs: Expr<Self>, rhs: Expr<HdlOption<Rhs>>) -> Expr<Bool> {
#[hdl]
let cmp_ne = wire();
#[hdl]
match lhs {
HdlSome(lhs) =>
{
#[hdl]
match rhs {
HdlSome(rhs) => connect(cmp_ne, ExprPartialEq::cmp_ne(lhs, rhs)),
HdlNone => connect(cmp_ne, true),
}
}
HdlNone =>
{
#[hdl]
match rhs {
HdlSome(_) => connect(cmp_ne, true),
HdlNone => connect(cmp_ne, false),
}
}
}
cmp_ne
}
}
#[allow(non_snake_case)]
pub fn HdlNone<T: StaticType>() -> Expr<HdlOption<T>> {
HdlOption[T::TYPE].HdlNone()

View file

@ -191,10 +191,14 @@ circuit check_array_repeat:
};
}
pub trait UnknownTrait {}
impl<T: ?Sized> UnknownTrait for T {}
#[hdl_module(outline_generated)]
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
where
T: StaticType,
T: StaticType + UnknownTrait,
ConstUsize<N>: KnownSize,
U: std::fmt::Display,
{
@ -376,18 +380,18 @@ circuit check_written_inside_both_if_else:
};
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct<T> {
pub a: T,
pub b: UInt<8>,
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct2 {
pub v: UInt<8>,
}
#[hdl(outline_generated)]
#[hdl(outline_generated, cmp_eq)]
pub struct TestStruct3 {}
#[hdl_module(outline_generated)]
@ -4421,3 +4425,125 @@ circuit check_let_patterns:
",
};
}
#[hdl_module(outline_generated)]
pub fn check_struct_cmp_eq() {
#[hdl]
let tuple_lhs: (UInt<1>, SInt<1>, Bool) = m.input();
#[hdl]
let tuple_rhs: (UInt<1>, SInt<1>, Bool) = m.input();
#[hdl]
let tuple_cmp_eq: Bool = m.output();
connect(tuple_cmp_eq, tuple_lhs.cmp_eq(tuple_rhs));
#[hdl]
let tuple_cmp_ne: Bool = m.output();
connect(tuple_cmp_ne, tuple_lhs.cmp_ne(tuple_rhs));
#[hdl]
let test_struct_lhs: TestStruct<SInt<8>> = m.input();
#[hdl]
let test_struct_rhs: TestStruct<SInt<8>> = m.input();
#[hdl]
let test_struct_cmp_eq: Bool = m.output();
connect(test_struct_cmp_eq, test_struct_lhs.cmp_eq(test_struct_rhs));
#[hdl]
let test_struct_cmp_ne: Bool = m.output();
connect(test_struct_cmp_ne, test_struct_lhs.cmp_ne(test_struct_rhs));
#[hdl]
let test_struct_2_lhs: TestStruct2 = m.input();
#[hdl]
let test_struct_2_rhs: TestStruct2 = m.input();
#[hdl]
let test_struct_2_cmp_eq: Bool = m.output();
connect(
test_struct_2_cmp_eq,
test_struct_2_lhs.cmp_eq(test_struct_2_rhs),
);
#[hdl]
let test_struct_2_cmp_ne: Bool = m.output();
connect(
test_struct_2_cmp_ne,
test_struct_2_lhs.cmp_ne(test_struct_2_rhs),
);
#[hdl]
let test_struct_3_lhs: TestStruct3 = m.input();
#[hdl]
let test_struct_3_rhs: TestStruct3 = m.input();
#[hdl]
let test_struct_3_cmp_eq: Bool = m.output();
connect(
test_struct_3_cmp_eq,
test_struct_3_lhs.cmp_eq(test_struct_3_rhs),
);
#[hdl]
let test_struct_3_cmp_ne: Bool = m.output();
connect(
test_struct_3_cmp_ne,
test_struct_3_lhs.cmp_ne(test_struct_3_rhs),
);
}
#[test]
fn test_struct_cmp_eq() {
let _n = SourceLocation::normalize_files_for_tests();
let m = check_struct_cmp_eq();
dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! {
m =>
"/test/check_struct_cmp_eq.fir": r"FIRRTL version 3.2.0
circuit check_struct_cmp_eq:
type Ty0 = {`0`: UInt<1>, `1`: SInt<1>, `2`: UInt<1>}
type Ty1 = {a: SInt<8>, b: UInt<8>}
type Ty2 = {v: UInt<8>}
type Ty3 = {}
module check_struct_cmp_eq: @[module-XXXXXXXXXX.rs 1:1]
input tuple_lhs: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input tuple_rhs: Ty0 @[module-XXXXXXXXXX.rs 3:1]
output tuple_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 4:1]
output tuple_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 6:1]
input test_struct_lhs: Ty1 @[module-XXXXXXXXXX.rs 8:1]
input test_struct_rhs: Ty1 @[module-XXXXXXXXXX.rs 9:1]
output test_struct_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 10:1]
output test_struct_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 12:1]
input test_struct_2_lhs: Ty2 @[module-XXXXXXXXXX.rs 14:1]
input test_struct_2_rhs: Ty2 @[module-XXXXXXXXXX.rs 15:1]
output test_struct_2_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 16:1]
output test_struct_2_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 18:1]
input test_struct_3_lhs: Ty3 @[module-XXXXXXXXXX.rs 20:1]
input test_struct_3_rhs: Ty3 @[module-XXXXXXXXXX.rs 21:1]
output test_struct_3_cmp_eq: UInt<1> @[module-XXXXXXXXXX.rs 22:1]
output test_struct_3_cmp_ne: UInt<1> @[module-XXXXXXXXXX.rs 24:1]
wire _array_literal_expr: UInt<1>[3]
connect _array_literal_expr[0], eq(tuple_lhs.`0`, tuple_rhs.`0`)
connect _array_literal_expr[1], eq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _array_literal_expr[2], eq(tuple_lhs.`2`, tuple_rhs.`2`)
wire _cast_array_to_bits_expr: UInt<1>[3]
connect _cast_array_to_bits_expr[0], _array_literal_expr[0]
connect _cast_array_to_bits_expr[1], _array_literal_expr[1]
connect _cast_array_to_bits_expr[2], _array_literal_expr[2]
wire _cast_to_bits_expr: UInt<3>
connect _cast_to_bits_expr, cat(_cast_array_to_bits_expr[2], cat(_cast_array_to_bits_expr[1], _cast_array_to_bits_expr[0]))
connect tuple_cmp_eq, andr(_cast_to_bits_expr) @[module-XXXXXXXXXX.rs 5:1]
wire _array_literal_expr_1: UInt<1>[3]
connect _array_literal_expr_1[0], neq(tuple_lhs.`0`, tuple_rhs.`0`)
connect _array_literal_expr_1[1], neq(tuple_lhs.`1`, tuple_rhs.`1`)
connect _array_literal_expr_1[2], neq(tuple_lhs.`2`, tuple_rhs.`2`)
wire _cast_array_to_bits_expr_1: UInt<1>[3]
connect _cast_array_to_bits_expr_1[0], _array_literal_expr_1[0]
connect _cast_array_to_bits_expr_1[1], _array_literal_expr_1[1]
connect _cast_array_to_bits_expr_1[2], _array_literal_expr_1[2]
wire _cast_to_bits_expr_1: UInt<3>
connect _cast_to_bits_expr_1, cat(_cast_array_to_bits_expr_1[2], cat(_cast_array_to_bits_expr_1[1], _cast_array_to_bits_expr_1[0]))
connect tuple_cmp_ne, orr(_cast_to_bits_expr_1) @[module-XXXXXXXXXX.rs 7:1]
connect test_struct_cmp_eq, and(eq(test_struct_lhs.a, test_struct_rhs.a), eq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 11:1]
connect test_struct_cmp_ne, or(neq(test_struct_lhs.a, test_struct_rhs.a), neq(test_struct_lhs.b, test_struct_rhs.b)) @[module-XXXXXXXXXX.rs 13:1]
connect test_struct_2_cmp_eq, eq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 17:1]
connect test_struct_2_cmp_ne, neq(test_struct_2_lhs.v, test_struct_2_rhs.v) @[module-XXXXXXXXXX.rs 19:1]
connect test_struct_3_cmp_eq, UInt<1>(0h1) @[module-XXXXXXXXXX.rs 23:1]
connect test_struct_3_cmp_ne, UInt<1>(0h0) @[module-XXXXXXXXXX.rs 25:1]
",
};
}