From 8e4eeef72340f1205ed5d2277dab112cdf352b35 Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Thu, 30 Apr 2026 23:10:49 -0700 Subject: [PATCH] add support for custom debug/display formatting of #[hdl] structs/enums also cleans up default debug formatting to use the struct/enum name (or MaskType) instead of the implementation detail type name. --- crates/fayalite-proc-macros-impl/src/fold.rs | 1 + .../src/hdl_bundle.rs | 128 ++++++++++--- .../fayalite-proc-macros-impl/src/hdl_enum.rs | 122 ++++++++++++- .../src/hdl_type_alias.rs | 28 ++- .../src/hdl_type_common.rs | 168 +++++++++++++++++- crates/fayalite-proc-macros-impl/src/lib.rs | 5 + crates/fayalite/src/array.rs | 17 +- crates/fayalite/src/bundle.rs | 30 +++- crates/fayalite/src/clock.rs | 14 +- crates/fayalite/src/enum_.rs | 13 +- crates/fayalite/src/int.rs | 31 +++- crates/fayalite/src/int/uint_in_range.rs | 20 ++- crates/fayalite/src/phantom_const.rs | 11 +- crates/fayalite/src/reset.rs | 14 +- crates/fayalite/src/sim/value.rs | 21 ++- crates/fayalite/src/ty.rs | 29 ++- crates/fayalite/tests/hdl_types_fmt.rs | 166 +++++++++++++++++ .../ui/simvalue_is_not_internable.stderr | 18 +- 18 files changed, 773 insertions(+), 63 deletions(-) create mode 100644 crates/fayalite/tests/hdl_types_fmt.rs diff --git a/crates/fayalite-proc-macros-impl/src/fold.rs b/crates/fayalite-proc-macros-impl/src/fold.rs index 22e7b82..50423b5 100644 --- a/crates/fayalite-proc-macros-impl/src/fold.rs +++ b/crates/fayalite-proc-macros-impl/src/fold.rs @@ -257,5 +257,6 @@ no_op_fold!(syn::Token![let]); no_op_fold!(syn::Token![mut]); no_op_fold!(syn::Token![static]); no_op_fold!(syn::Token![struct]); +no_op_fold!(syn::Token![type]); no_op_fold!(syn::Token![where]); no_op_fold!(usize); diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index 97fa3ff..6847af9 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -3,8 +3,9 @@ use crate::{ Errors, HdlAttr, PairsIterExt, hdl_type_common::{ - ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField, ParsedFieldsNamed, ParsedGenerics, - SplitForImpl, TypesParser, WrappedInConst, common_derives, get_target, + CustomDebugOptions, CustomDebugTrait, ItemOptions, MakeHdlTypeExpr, MaybeParsed, + ParsedField, ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst, + common_derives, create_struct_debug_impl, get_target, }, kw, }; @@ -30,6 +31,7 @@ pub(crate) struct ParsedBundle { pub(crate) fields: MaybeParsed, pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, + pub(crate) mask_type_name: String, pub(crate) mask_type_match_variant_ident: Ident, pub(crate) mask_type_sim_value_ident: Ident, pub(crate) match_variant_ident: Ident, @@ -88,6 +90,8 @@ impl ParsedBundle { no_runtime_generics: _, cmp_eq: _, ref get, + custom_debug: _, + custom_sim_display: _, } = options.body; if let Some((get, ..)) = get { errors.error(get, "#[hdl(get(...))] is not allowed on structs"); @@ -131,6 +135,7 @@ impl ParsedBundle { fields, field_flips, mask_type_ident: format_ident!("__{}__MaskType", ident), + mask_type_name: format!("MaskType<{}>", ident), mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident), mask_type_sim_value_ident: format_ident!("__{}__MaskType__SimValue", ident), match_variant_ident: format_ident!("__{}__MatchVariant", ident), @@ -448,6 +453,7 @@ impl ToTokens for ParsedBundle { fields, field_flips, mask_type_ident, + mask_type_name, mask_type_match_variant_ident, mask_type_sim_value_ident, match_variant_ident, @@ -464,11 +470,20 @@ impl ToTokens for ParsedBundle { no_runtime_generics, cmp_eq, get: _, + custom_debug: _, + custom_sim_display, } = &options.body; + let CustomDebugOptions { + type_: custom_debug_type, + sim: custom_debug_sim, + mask_type: custom_debug_mask_type, + mask_sim: custom_debug_mask_sim, + } = options.body.custom_debug(); let target = get_target(target, ident); + let struct_name = ident.to_string(); let mut item_attrs = attrs.clone(); - item_attrs.push(common_derives(span)); - ItemStruct { + item_attrs.push(common_derives(span, false)); + let type_struct = ItemStruct { attrs: item_attrs, vis: vis.clone(), struct_token: *struct_token, @@ -476,8 +491,8 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(fields.clone().into()), semi_token: None, - } - .to_tokens(tokens); + }; + type_struct.to_tokens(tokens); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) = (generics, fields, no_runtime_generics) @@ -503,6 +518,9 @@ impl ToTokens for ParsedBundle { } let mut wrapped_in_const = WrappedInConst::new(tokens, span); let tokens = wrapped_in_const.inner(); + if custom_debug_type.is_none() { + create_struct_debug_impl(&type_struct, &struct_name, None).to_tokens(tokens); + } let builder = Builder { vis: vis.clone(), struct_token: *struct_token, @@ -530,9 +548,9 @@ impl ToTokens for ParsedBundle { mask_type_builder.to_tokens(tokens); let unfilled_mask_type_builder_ty = mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled); - ItemStruct { + let mask_type_struct = ItemStruct { attrs: vec![ - common_derives(span), + common_derives(span, false), parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, @@ -543,17 +561,20 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(mask_type_fields.clone()), semi_token: None, + }; + mask_type_struct.to_tokens(tokens); + if custom_debug_mask_type.is_none() { + create_struct_debug_impl(&mask_type_struct, mask_type_name, None).to_tokens(tokens); } - .to_tokens(tokens); let mut mask_type_match_variant_fields = mask_type_fields.clone(); for Field { ty, .. } in &mut mask_type_match_variant_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> }; } - ItemStruct { + let mask_type_match_variant_struct = ItemStruct { attrs: vec![ - common_derives(span), + common_derives(span, false), parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, @@ -564,17 +585,19 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(mask_type_match_variant_fields), semi_token: None, - } - .to_tokens(tokens); + }; + mask_type_match_variant_struct.to_tokens(tokens); + create_struct_debug_impl(&mask_type_match_variant_struct, mask_type_name, None) + .to_tokens(tokens); let mut match_variant_fields = FieldsNamed::from(fields.clone()); for Field { ty, .. } in &mut match_variant_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::expr::Expr<#ty> }; } - ItemStruct { + let match_variant_struct = ItemStruct { attrs: vec![ - common_derives(span), + common_derives(span, false), parse_quote_spanned! {span=> #[allow(non_camel_case_types, dead_code)] }, @@ -585,19 +608,19 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(match_variant_fields), semi_token: None, - } - .to_tokens(tokens); + }; + match_variant_struct.to_tokens(tokens); + create_struct_debug_impl(&match_variant_struct, &struct_name, None).to_tokens(tokens); let mut mask_type_sim_value_fields = mask_type_fields; for Field { ty, .. } in &mut mask_type_sim_value_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::sim::value::SimValue<#ty> }; } - ItemStruct { + let mask_type_sim_value_struct = ItemStruct { attrs: vec![ parse_quote_spanned! {span=> #[::fayalite::__std::prelude::v1::derive( - ::fayalite::__std::fmt::Debug, ::fayalite::__std::clone::Clone, )] }, @@ -611,19 +634,34 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(mask_type_sim_value_fields), semi_token: None, + }; + mask_type_sim_value_struct.to_tokens(tokens); + if custom_debug_mask_sim.is_none() { + create_struct_debug_impl( + &mask_type_struct, + mask_type_name, + Some(CustomDebugTrait { + trait_path: &parse_quote_spanned! {span=> + ::fayalite::ty::SimValueDebug + }, + fn_name: &format_ident!("sim_value_debug", span = span), + this_arg: &parse_quote_spanned! {span=> + value: &::SimValue + }, + }), + ) + .to_tokens(tokens); } - .to_tokens(tokens); let mut sim_value_fields = FieldsNamed::from(fields.clone()); for Field { ty, .. } in &mut sim_value_fields.named { *ty = parse_quote_spanned! {span=> ::fayalite::sim::value::SimValue<#ty> }; } - ItemStruct { + let sim_value_struct = ItemStruct { attrs: vec![ parse_quote_spanned! {span=> #[::fayalite::__std::prelude::v1::derive( - ::fayalite::__std::fmt::Debug, ::fayalite::__std::clone::Clone, )] }, @@ -637,8 +675,36 @@ impl ToTokens for ParsedBundle { generics: generics.into(), fields: Fields::Named(sim_value_fields), semi_token: None, + }; + sim_value_struct.to_tokens(tokens); + if custom_debug_sim.is_none() { + create_struct_debug_impl( + &type_struct, + &struct_name, + Some(CustomDebugTrait { + trait_path: &parse_quote_spanned! {span=> + ::fayalite::ty::SimValueDebug + }, + fn_name: &format_ident!("sim_value_debug", span = span), + this_arg: &parse_quote_spanned! {span=> + value: &::SimValue + }, + }), + ) + .to_tokens(tokens); + } + if custom_sim_display.is_some() { + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics + #where_clause + { + fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + <#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f) + } + } + }.to_tokens(tokens); } - .to_tokens(tokens); let this_token = Ident::new("__this", span); let fields_token = Ident::new("__fields", span); let self_token = Token![self](span); @@ -820,6 +886,14 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] + impl #impl_generics ::fayalite::__std::fmt::Debug for #mask_type_sim_value_ident #type_generics + #where_clause + { + fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + <#mask_type_ident #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f) + } + } + #[automatically_derived] impl #impl_generics ::fayalite::expr::ValueType for #mask_type_sim_value_ident #type_generics #where_clause { @@ -980,6 +1054,14 @@ impl ToTokens for ParsedBundle { } } #[automatically_derived] + impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics + #where_clause + { + fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + <#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f) + } + } + #[automatically_derived] impl #impl_generics ::fayalite::expr::ValueType for #sim_value_ident #type_generics #where_clause { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index ae0f8f4..e2410bc 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -3,8 +3,9 @@ use crate::{ Errors, HdlAttr, PairsIterExt, hdl_type_common::{ - ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl, - TypesParser, WrappedInConst, common_derives, get_target, + CustomDebugOptions, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, + SplitForImpl, TypesParser, WrappedInConst, common_derives, create_struct_debug_impl, + get_target, }, kw, }; @@ -160,6 +161,8 @@ impl ParsedEnum { no_runtime_generics: _, cmp_eq, ref get, + custom_debug: _, + custom_sim_display: _, } = options.body; if let Some((cmp_eq,)) = cmp_eq { errors.error(cmp_eq, "#[hdl(cmp_eq)] is not yet implemented for enums"); @@ -167,6 +170,24 @@ impl ParsedEnum { if let Some((get, ..)) = get { errors.error(get, "#[hdl(get(...))] is not allowed on enums"); } + let CustomDebugOptions { + type_: _, + sim: _, + mask_type, + mask_sim, + } = options.body.custom_debug(); + if let Some((mask_type,)) = mask_type { + errors.error( + mask_type, + "#[hdl(custom_debug(mask_type)] is not allowed on enums", + ); + } + if let Some((mask_sim,)) = mask_sim { + errors.error( + mask_sim, + "#[hdl(custom_debug(mask_sim)] is not allowed on enums", + ); + } attrs.retain(|attr| { if attr.path().is_ident("repr") { errors.error(attr, "#[repr] is not supported on #[hdl] enums"); @@ -230,10 +251,19 @@ impl ToTokens for ParsedEnum { no_runtime_generics, cmp_eq: _, // TODO: implement cmp_eq for enums get: _, + custom_debug: _, + custom_sim_display, } = &options.body; + let CustomDebugOptions { + type_: custom_debug_type, + sim: custom_debug_sim, + mask_type: _, + mask_sim: _, + } = options.body.custom_debug(); let target = get_target(target, ident); + let enum_name = ident.to_string(); let mut struct_attrs = attrs.clone(); - struct_attrs.push(common_derives(span)); + struct_attrs.push(common_derives(span, false)); struct_attrs.push(parse_quote_spanned! {span=> #[allow(non_snake_case)] }); @@ -273,7 +303,7 @@ impl ToTokens for ParsedEnum { } }, )); - ItemStruct { + let type_struct = ItemStruct { attrs: struct_attrs, vis: vis.clone(), struct_token: Token![struct](enum_token.span), @@ -288,8 +318,8 @@ impl ToTokens for ParsedEnum { }) }, semi_token: None, - } - .to_tokens(tokens); + }; + type_struct.to_tokens(tokens); let (impl_generics, type_generics, where_clause) = generics.split_for_impl(); if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) { generics.make_runtime_generics(tokens, vis, ident, &target, |context| { @@ -373,6 +403,9 @@ impl ToTokens for ParsedEnum { } .to_tokens(tokens); } + if custom_debug_type.is_none() { + create_struct_debug_impl(&type_struct, &enum_name, None).to_tokens(tokens); + } let mut enum_attrs = attrs.clone(); enum_attrs.push(parse_quote_spanned! {span=> #[allow(dead_code, non_camel_case_types)] @@ -453,7 +486,6 @@ impl ToTokens for ParsedEnum { let mut enum_attrs = attrs.clone(); enum_attrs.push(parse_quote_spanned! {span=> #[::fayalite::__std::prelude::v1::derive( - ::fayalite::__std::fmt::Debug, ::fayalite::__std::clone::Clone, )] }); @@ -838,6 +870,74 @@ impl ToTokens for ParsedEnum { }, )), ); + if custom_debug_sim.is_none() { + let debug_match_arms = Vec::from_iter( + variants + .iter() + .map( + |ParsedVariant { + attrs: _, + options: _, + ident, + field, + }| { + let variant_name = ident.to_string(); + if let Some(_) = field { + quote_spanned! {span=> + #sim_value_ident::#ident(field, _) => { + f.debug_tuple(#variant_name).field(field).finish() + } + } + } else { + quote_spanned! {span=> + #sim_value_ident::#ident(_) => { + f.write_str(#variant_name) + } + } + } + }, + ) + .chain(sim_value_unknown_variant_name.as_ref().map( + |sim_value_unknown_variant_name| { + let sim_value_unknown_variant_name_str = + sim_value_unknown_variant_name.to_string(); + quote_spanned! {span=> + #sim_value_ident::#sim_value_unknown_variant_name(_) => { + f.write_str(#sim_value_unknown_variant_name_str) + } + } + }, + )), + ); + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::fayalite::ty::SimValueDebug for #target #type_generics + #where_clause + { + fn sim_value_debug( + value: &::SimValue, + f: &mut ::fayalite::__std::fmt::Formatter<'_>, + ) -> ::fayalite::__std::fmt::Result { + match value { + #(#debug_match_arms)* + } + } + } + } + .to_tokens(tokens); + } + if custom_sim_display.is_some() { + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics ::fayalite::__std::fmt::Display for #sim_value_ident #type_generics + #where_clause + { + fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + <#target #type_generics as ::fayalite::ty::SimValueDisplay>::sim_value_display(self, f) + } + } + }.to_tokens(tokens); + } let variants_len = variants.len(); quote_spanned! {span=> #[automatically_derived] @@ -934,6 +1034,14 @@ impl ToTokens for ParsedEnum { } } #[automatically_derived] + impl #impl_generics ::fayalite::__std::fmt::Debug for #sim_value_ident #type_generics + #where_clause + { + fn fmt(&self, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + <#target #type_generics as ::fayalite::ty::SimValueDebug>::sim_value_debug(self, f) + } + } + #[automatically_derived] impl #impl_generics ::fayalite::sim::value::ToSimValueWithType<#target #type_generics> for #sim_value_ident #type_generics #where_clause diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs index 0fa2222..556c15b 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -215,6 +215,8 @@ impl ParsedTypeAlias { no_runtime_generics, cmp_eq, get: _, + ref custom_debug, + custom_sim_display, } = options.body; if let Some((no_static,)) = no_static { errors.error(no_static, "no_static is not valid on type aliases"); @@ -234,6 +236,15 @@ impl ParsedTypeAlias { if let Some((cmp_eq,)) = cmp_eq { errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); } + if let Some((custom_debug, _, _)) = custom_debug { + errors.error(custom_debug, "custom_debug is not valid on type aliases"); + } + if let Some((custom_sim_display,)) = custom_sim_display { + errors.error( + custom_sim_display, + "custom_sim_display is not valid on type aliases", + ); + } if let Some((custom_bounds,)) = custom_bounds { errors.error( custom_bounds, @@ -287,6 +298,8 @@ impl ParsedTypeAlias { no_runtime_generics: _, cmp_eq, ref mut get, + ref custom_debug, + custom_sim_display, } = options.body; if let Some(get) = get.take() { return Self::parse_phantom_const_accessor( @@ -311,6 +324,15 @@ impl ParsedTypeAlias { if let Some((cmp_eq,)) = cmp_eq { errors.error(cmp_eq, "cmp_eq is not valid on type aliases"); } + if let Some((custom_debug, _, _)) = custom_debug { + errors.error(custom_debug, "custom_debug is not valid on type aliases"); + } + if let Some((custom_sim_display,)) = custom_sim_display { + errors.error( + custom_sim_display, + "custom_sim_display 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)) { @@ -356,6 +378,8 @@ impl ToTokens for ParsedTypeAlias { no_runtime_generics, cmp_eq: _, get: _, + custom_debug: _, + custom_sim_display: _, } = &options.body; let target = get_target(target, ident); let mut type_attrs = attrs.clone(); @@ -402,6 +426,8 @@ impl ToTokens for ParsedTypeAlias { no_runtime_generics: _, cmp_eq: _, get: _, + custom_debug: _, + custom_sim_display: _, } = &options.body; let span = ident.span(); let mut type_attrs = attrs.clone(); @@ -427,7 +453,7 @@ impl ToTokens for ParsedTypeAlias { format_ident!("__{}__GenericsAccumulation", ident); ItemStruct { attrs: vec![ - common_derives(span), + common_derives(span, true), parse_quote_spanned! {span=> #[allow(non_camel_case_types)] }, diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 3a0e5e9..18cffc6 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -7,10 +7,10 @@ use std::{collections::HashMap, fmt, mem}; use syn::{ AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup, ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed, - FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct, - Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, Turbofish, - Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, TypeTuple, - Visibility, WhereClause, WherePredicate, + FieldsUnnamed, FnArg, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, + ItemStruct, Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, + Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, + TypeTuple, Visibility, WhereClause, WherePredicate, parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, punctuated::{Pair, Punctuated}, @@ -18,6 +18,17 @@ use syn::{ token::{Brace, Bracket, Paren}, }; +crate::options! { + #[options = CustomDebugOptions] + #[no_ident_fragment] + pub(crate) enum CustomDebugOption { + Type(type_), + Sim(sim), + MaskType(mask_type), + MaskSim(mask_sim), + } +} + crate::options! { #[options = ItemOptions] pub(crate) enum ItemOption { @@ -28,6 +39,8 @@ crate::options! { NoRuntimeGenerics(no_runtime_generics), CmpEq(cmp_eq), Get(get, Expr), + CustomDebug(custom_debug, CustomDebugOptions), + CustomSimDisplay(custom_sim_display), } } @@ -41,8 +54,36 @@ impl ItemOptions { { self.no_static = Some((kw::no_static(custom_bounds.span),)); } + if let Some((kw, _, custom_debug)) = &mut self.custom_debug { + if let CustomDebugOptions { + type_: None, + sim: None, + mask_type: None, + mask_sim: None, + } = custom_debug + { + *custom_debug = CustomDebugOptions { + type_: Some((kw::type_(kw.span),)), + sim: Some((kw::sim(kw.span),)), + mask_type: None, + mask_sim: None, + }; + } + } Ok(()) } + pub(crate) fn custom_debug(&self) -> &CustomDebugOptions { + self.custom_debug.as_ref().map(|v| &v.2).unwrap_or( + const { + &CustomDebugOptions { + type_: None, + sim: None, + mask_type: None, + mask_sim: None, + } + }, + ) + } } pub(crate) struct WrappedInConst<'a> { @@ -84,10 +125,17 @@ pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: } } -pub(crate) fn common_derives(span: Span) -> Attribute { +pub(crate) fn common_derives(span: Span, include_debug: bool) -> Attribute { + let debug = include_debug + .then(|| { + quote_spanned! {span=> + ::fayalite::__std::fmt::Debug + } + }) + .into_iter(); parse_quote_spanned! {span=> #[::fayalite::__std::prelude::v1::derive( - ::fayalite::__std::fmt::Debug, + #(#debug,)* ::fayalite::__std::cmp::Eq, ::fayalite::__std::cmp::PartialEq, ::fayalite::__std::hash::Hash, @@ -2975,7 +3023,7 @@ impl ParsedGenerics { let span = ident.span(); ItemStruct { attrs: vec![ - common_derives(span), + common_derives(span, true), parse_quote_spanned! {span=> #[allow(non_camel_case_types)] }, @@ -4733,3 +4781,109 @@ impl ParsedVisibility { .map(|ord| if ord.is_lt() { self } else { other }) } } + +pub(crate) struct CustomDebugTrait<'a> { + pub(crate) trait_path: &'a Path, + pub(crate) fn_name: &'a Ident, + pub(crate) this_arg: &'a FnArg, +} + +#[must_use] +pub(crate) fn create_struct_debug_impl( + item_struct: &ItemStruct, + debug_struct_name: &str, + custom_debug_trait: Option>, +) -> TokenStream { + let ident = &item_struct.ident; + let span = ident.span(); + let (impl_generics, type_generics, where_clause) = item_struct.generics.split_for_impl(); + let trait_path; + let fn_name; + let this_arg; + let CustomDebugTrait { + trait_path, + fn_name, + this_arg, + } = match custom_debug_trait { + Some(v) => v, + None => { + trait_path = parse_quote_spanned! {span=> + ::fayalite::__std::fmt::Debug + }; + fn_name = parse_quote_spanned! {span=> + fmt + }; + this_arg = parse_quote_spanned! {span=> + &self + }; + CustomDebugTrait { + trait_path: &trait_path, + fn_name: &fn_name, + this_arg: &this_arg, + } + } + }; + let this_arg_name = match this_arg { + FnArg::Receiver(this_arg) => this_arg.self_token.to_token_stream(), + FnArg::Typed(this_arg) => match &*this_arg.pat { + syn::Pat::Ident(pat_ident) => pat_ident.ident.to_token_stream(), + _ => unreachable!(), + }, + }; + match &item_struct.fields { + Fields::Named(fields) => { + let field_idents = fields + .named + .iter() + .map(|v| v.ident.as_ref().expect("known to have field name")); + let field_names = field_idents.clone().map(|v| v.to_string()); + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics #trait_path for #ident #type_generics + #where_clause + { + fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + let _ = #this_arg_name; + f.debug_struct(#debug_struct_name) + #(.field(#field_names, &#this_arg_name.#field_idents))* + .finish() + } + } + } + } + Fields::Unnamed(fields) => { + let field_members = fields + .unnamed + .iter() + .enumerate() + .map(|(index, _)| syn::Index { + index: index as _, + span, + }); + quote_spanned! {span=> + #[automatically_derived] + impl #impl_generics #trait_path for #ident #type_generics + #where_clause + { + fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + let _ = #this_arg_name; + f.debug_tuple(#debug_struct_name) + #(.field(&#this_arg_name.#field_members))* + .finish() + } + } + } + } + Fields::Unit => quote_spanned! {ident.span()=> + #[automatically_derived] + impl #impl_generics #trait_path for #ident #type_generics + #where_clause + { + fn #fn_name(#this_arg, f: &mut ::fayalite::__std::fmt::Formatter<'_>) -> ::fayalite::__std::fmt::Result { + let _ = #this_arg_name; + f.write_str(#debug_struct_name) + } + } + }, + } +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 152053c..905cb05 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -42,6 +42,7 @@ pub(crate) trait CustomToken: mod kw { pub(crate) use syn::token::Extern as extern_; + pub(crate) use syn::token::Type as type_; macro_rules! custom_keyword { ($kw:ident) => { @@ -75,6 +76,8 @@ mod kw { custom_keyword!(cmp_eq); custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); + custom_keyword!(custom_debug); + custom_keyword!(custom_sim_display); custom_keyword!(flip); custom_keyword!(get); custom_keyword!(hdl); @@ -83,6 +86,8 @@ mod kw { custom_keyword!(input); custom_keyword!(instance); custom_keyword!(m); + custom_keyword!(mask_sim); + custom_keyword!(mask_type); custom_keyword!(memory); custom_keyword!(memory_array); custom_keyword!(memory_with_init); diff --git a/crates/fayalite/src/array.rs b/crates/fayalite/src/array.rs index 6ca6809..fa754fd 100644 --- a/crates/fayalite/src/array.rs +++ b/crates/fayalite/src/array.rs @@ -13,13 +13,13 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValueSlice, OpaqueSimValueWriter, - OpaqueSimValueWritten, StaticType, Type, TypeProperties, TypeWithDeref, + OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, TypeWithDeref, serde_impls::SerdeCanonicalType, }, util::ConstUsize, }; use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error}; -use std::{borrow::Cow, iter::FusedIterator, ops::Index}; +use std::{borrow::Cow, fmt, iter::FusedIterator, ops::Index}; #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct ArrayType { @@ -28,8 +28,8 @@ pub struct ArrayType { type_properties: TypeProperties, } -impl std::fmt::Debug for ArrayType { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for ArrayType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Array<{:?}, {}>", self.element, self.len()) } } @@ -182,6 +182,15 @@ impl, Len: Size, State: Visitor + ?Sized> Visit } } +impl SimValueDebug for ArrayType { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl Type for ArrayType { type BaseType = Array; type MaskType = ArrayType; diff --git a/crates/fayalite/src/bundle.rs b/crates/fayalite/src/bundle.rs index 1471f3a..5fad35c 100644 --- a/crates/fayalite/src/bundle.rs +++ b/crates/fayalite/src/bundle.rs @@ -14,8 +14,8 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, MatchVariantWithoutScope, OpaqueSimValue, OpaqueSimValueSize, - OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, - TypeProperties, TypeWithDeref, impl_match_variant_as_self, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug, + StaticType, Type, TypeProperties, TypeWithDeref, impl_match_variant_as_self, }, util::HashMap, }; @@ -271,6 +271,15 @@ impl Type for Bundle { } } +impl SimValueDebug for Bundle { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + pub trait BundleType: Type { type Builder: Default; fn fields(&self) -> Interned<[BundleField]>; @@ -471,6 +480,14 @@ macro_rules! impl_tuples { #[var($var)] })*] } + impl<$($T: Type,)*> SimValueDebug for ($($T,)*) { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } + } impl<$($T: Type,)*> Type for ($($T,)*) { type BaseType = Bundle; type MaskType = ($($T::MaskType,)*); @@ -773,6 +790,15 @@ impl_tuples! { ] } +impl SimValueDebug for PhantomData { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl Type for PhantomData { type BaseType = Bundle; type MaskType = (); diff --git a/crates/fayalite/src/clock.rs b/crates/fayalite/src/clock.rs index 168142b..0e6d145 100644 --- a/crates/fayalite/src/clock.rs +++ b/crates/fayalite/src/clock.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ expr::{Expr, ValueType}, hdl, @@ -9,10 +10,12 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, - OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, + impl_match_variant_as_self, }, }; use bitvec::{bits, order::Lsb0}; +use std::fmt; #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)] pub struct Clock; @@ -69,6 +72,15 @@ impl Type for Clock { } } +impl SimValueDebug for Clock { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl Clock { pub fn type_properties(self) -> TypeProperties { Self::TYPE_PROPERTIES diff --git a/crates/fayalite/src/enum_.rs b/crates/fayalite/src/enum_.rs index a04f67a..d545f93 100644 --- a/crates/fayalite/src/enum_.rs +++ b/crates/fayalite/src/enum_.rs @@ -14,8 +14,8 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, MatchVariantAndInactiveScope, OpaqueSimValue, OpaqueSimValueSize, - OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, - TypeProperties, + OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug, + StaticType, Type, TypeProperties, }, util::HashMap, }; @@ -410,6 +410,15 @@ impl Type for Enum { } } +impl SimValueDebug for Enum { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug, Default)] pub struct EnumPaddingSimValue { bits: Option, diff --git a/crates/fayalite/src/int.rs b/crates/fayalite/src/int.rs index c461306..ff20933 100644 --- a/crates/fayalite/src/int.rs +++ b/crates/fayalite/src/int.rs @@ -15,8 +15,8 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, FillInDefaultedGenerics, OpaqueSimValueSize, OpaqueSimValueSlice, - OpaqueSimValueWriter, OpaqueSimValueWritten, StaticType, Type, TypeProperties, - impl_match_variant_as_self, + OpaqueSimValueWriter, OpaqueSimValueWritten, SimValueDebug, SimValueDisplay, StaticType, + Type, TypeProperties, impl_match_variant_as_self, }, util::{ConstBool, ConstUsize, GenericConstBool, GenericConstUsize, interned_bit, slice_range}, }; @@ -1019,6 +1019,24 @@ macro_rules! impl_int { } } + impl SimValueDebug for $name { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } + } + + impl SimValueDisplay for $name { + fn sim_value_display( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Display::fmt(value, f) + } + } + impl Default for $name { fn default() -> Self { Self::TYPE @@ -1899,6 +1917,15 @@ impl Type for Bool { } } +impl SimValueDebug for Bool { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl StaticType for Bool { const TYPE: Self = Bool; const MASK_TYPE: Self::MaskType = Bool; diff --git a/crates/fayalite/src/int/uint_in_range.rs b/crates/fayalite/src/int/uint_in_range.rs index acf2fec..edf2e25 100644 --- a/crates/fayalite/src/int/uint_in_range.rs +++ b/crates/fayalite/src/int/uint_in_range.rs @@ -14,7 +14,7 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, - StaticType, Type, TypeProperties, impl_match_variant_as_self, + SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self, }, }; use bitvec::{order::Lsb0, view::BitView}; @@ -94,6 +94,15 @@ impl Type for UIntInRangeMaskType { } } +impl SimValueDebug for UIntInRangeMaskType { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl BundleType for UIntInRangeMaskType { type Builder = NoBuilder; @@ -339,6 +348,15 @@ macro_rules! define_uint_in_range_type { } } + impl SimValueDebug for $UIntInRangeType { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } + } + impl fmt::Debug for $UIntInRangeType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { value, range } = self; diff --git a/crates/fayalite/src/phantom_const.rs b/crates/fayalite/src/phantom_const.rs index fb7be6f..ba85817 100644 --- a/crates/fayalite/src/phantom_const.rs +++ b/crates/fayalite/src/phantom_const.rs @@ -9,7 +9,7 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSlice, OpaqueSimValueWriter, OpaqueSimValueWritten, - StaticType, Type, TypeProperties, impl_match_variant_as_self, + SimValueDebug, StaticType, Type, TypeProperties, impl_match_variant_as_self, serde_impls::{SerdeCanonicalType, SerdePhantomConst}, }, }; @@ -327,6 +327,15 @@ impl Type for PhantomConst { } } +impl SimValueDebug for PhantomConst { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl Default for PhantomConst where Interned: Default, diff --git a/crates/fayalite/src/reset.rs b/crates/fayalite/src/reset.rs index 13273ac..ddc3651 100644 --- a/crates/fayalite/src/reset.rs +++ b/crates/fayalite/src/reset.rs @@ -1,5 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information + use crate::{ clock::Clock, expr::{CastToImpl, Expr, ValueType}, @@ -8,11 +9,13 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValueSize, OpaqueSimValueSlice, OpaqueSimValueWriter, - OpaqueSimValueWritten, StaticType, Type, TypeProperties, impl_match_variant_as_self, + OpaqueSimValueWritten, SimValueDebug, StaticType, Type, TypeProperties, + impl_match_variant_as_self, }, util::ConstUsize, }; use bitvec::{bits, order::Lsb0}; +use std::fmt; mod sealed { pub trait ResetTypeSealed {} @@ -100,6 +103,15 @@ macro_rules! reset_type { } } + impl SimValueDebug for $name { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } + } + impl $name { pub fn type_properties(self) -> TypeProperties { Self::TYPE_PROPERTIES diff --git a/crates/fayalite/src/sim/value.rs b/crates/fayalite/src/sim/value.rs index b6a4e4b..a127878 100644 --- a/crates/fayalite/src/sim/value.rs +++ b/crates/fayalite/src/sim/value.rs @@ -15,7 +15,8 @@ use crate::{ source_location::SourceLocation, ty::{ CanonicalType, OpaqueSimValue, OpaqueSimValueSize, OpaqueSimValueSlice, - OpaqueSimValueWriter, StaticType, Type, TypeProperties, impl_match_variant_as_self, + OpaqueSimValueWriter, SimValueDebug, StaticType, Type, TypeProperties, + impl_match_variant_as_self, }, util::{ ConstUsize, HashMap, @@ -1394,6 +1395,15 @@ impl Type for DynSimOnly { } } +impl SimValueDebug for DynSimOnly { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl Type for SimOnly { type BaseType = DynSimOnly; type MaskType = Bool; @@ -1459,6 +1469,15 @@ impl Type for SimOnly { } } +impl SimValueDebug for SimOnly { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + impl StaticType for SimOnly { const TYPE: Self = Self::new(); diff --git a/crates/fayalite/src/ty.rs b/crates/fayalite/src/ty.rs index 76c0955..ed0ba03 100644 --- a/crates/fayalite/src/ty.rs +++ b/crates/fayalite/src/ty.rs @@ -367,7 +367,15 @@ impl TypeOrDefault for crate::__ { } pub trait Type: - Copy + Hash + Eq + fmt::Debug + Send + Sync + 'static + FillInDefaultedGenerics + Copy + + Hash + + Eq + + fmt::Debug + + Send + + Sync + + 'static + + FillInDefaultedGenerics + + SimValueDebug { type BaseType: BaseType; type MaskType: Type; @@ -402,6 +410,16 @@ pub trait Type: ) -> OpaqueSimValueWritten<'w>; } +pub trait SimValueDebug { + fn sim_value_debug(value: &::SimValue, f: &mut fmt::Formatter<'_>) -> fmt::Result + where + Self: Type; +} + +pub trait SimValueDisplay: Type { + fn sim_value_display(value: &Self::SimValue, f: &mut fmt::Formatter<'_>) -> fmt::Result; +} + pub trait BaseType: Type< BaseType = Self, @@ -490,6 +508,15 @@ impl Type for CanonicalType { } } +impl SimValueDebug for CanonicalType { + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + fmt::Debug::fmt(value, f) + } +} + #[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize, Default)] #[non_exhaustive] pub struct OpaqueSimValueSizeRange { diff --git a/crates/fayalite/tests/hdl_types_fmt.rs b/crates/fayalite/tests/hdl_types_fmt.rs new file mode 100644 index 0000000..382f64d --- /dev/null +++ b/crates/fayalite/tests/hdl_types_fmt.rs @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use fayalite::{prelude::*, ty::SimValueDebug}; +use std::fmt; + +#[hdl(outline_generated)] +struct MyStruct0 { + v: T, + a: ArrayType, S>, +} + +#[hdl] +#[test] +fn check_my_struct0() { + let ty = MyStruct0[UInt[8]][3]; + assert_eq!( + format!("{ty:?}"), + "MyStruct0 { v: UInt<8>, a: Array, 3> }", + ); + assert_eq!( + format!("{:?}", ty.mask_type()), + "MaskType { v: Bool, a: Array }", + ); + let v = #[hdl(sim)] + MyStruct0::<_, _> { + v: 0x23u8, + a: [1u8, 2, 3], + }; + assert_eq!( + format!("{v:?}"), + "MyStruct0 { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }", + ); +} + +#[hdl(outline_generated, custom_debug())] +struct MyStruct1 { + v: T, + a: ArrayType, S>, +} + +impl fmt::Debug for MyStruct1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { v, a } = self; + f.debug_struct("Custom") + .field("v", v) + .field("a", a) + .finish() + } +} + +impl SimValueDebug for MyStruct1 { + #[hdl] + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + #[hdl(sim)] + let Self { v, a } = value; + f.debug_struct("Custom") + .field("v", &v) + .field("a", &a) + .finish() + } +} + +#[hdl] +#[test] +fn check_my_struct1() { + let ty = MyStruct1[UInt[8]][3]; + assert_eq!( + format!("{ty:?}"), + "Custom { v: UInt<8>, a: Array, 3> }", + ); + assert_eq!( + format!("{:?}", ty.mask_type()), + "MaskType { v: Bool, a: Array }", + ); + let v = #[hdl(sim)] + MyStruct1::<_, _> { + v: 0x23u8, + a: [1u8, 2, 3], + }; + assert_eq!( + format!("{v:?}"), + "Custom { v: 0x23_u8, a: [0x1_u8, 0x2_u8, 0x3_u8] }", + ); +} + +#[hdl(outline_generated)] +enum MyEnum0 { + Unit, + V(T), + A(ArrayType, S>), +} + +#[hdl] +#[test] +fn check_my_enum0() { + let ty = MyEnum0[UInt[8]][3]; + assert_eq!( + format!("{ty:?}"), + "MyEnum0 { Unit: (), V: UInt<8>, A: Array, 3> }", + ); + let v = #[hdl(sim)] + ty.Unit(); + assert_eq!(format!("{v:?}"), "Unit"); + let v = #[hdl(sim)] + ty.V(0x23u8); + assert_eq!(format!("{v:?}"), "V(0x23_u8)"); + let v = #[hdl(sim)] + ty.A([1u8, 2, 3]); + assert_eq!(format!("{v:?}"), "A([0x1_u8, 0x2_u8, 0x3_u8])"); +} + +#[hdl(outline_generated, custom_debug())] +enum MyEnum1 { + Unit, + V(T), + A(ArrayType, S>), +} + +impl fmt::Debug for MyEnum1 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let Self { Unit, V, A } = self; + f.debug_struct("Custom") + .field("Unit", Unit) + .field("V", V) + .field("A", A) + .finish() + } +} + +impl SimValueDebug for MyEnum1 { + #[hdl] + fn sim_value_debug( + value: &::SimValue, + f: &mut fmt::Formatter<'_>, + ) -> fmt::Result { + type SimValueT = ::SimValue; + match value { + SimValueT::::Unit(_) => f.write_str("MyEnum1::Unit"), + SimValueT::::V(v, _) => f.debug_tuple("MyEnum1::V").field(v).finish(), + SimValueT::::A(a, _) => f.debug_tuple("MyEnum1::A").field(a).finish(), + SimValueT::::Unknown(_) => f.write_str("MyEnum1::Unknown"), + } + } +} + +#[hdl] +#[test] +fn check_my_enum1() { + let ty = MyEnum1[UInt[8]][3]; + assert_eq!( + format!("{ty:?}"), + "Custom { Unit: (), V: UInt<8>, A: Array, 3> }", + ); + let v = #[hdl(sim)] + ty.Unit(); + assert_eq!(format!("{v:?}"), "MyEnum1::Unit"); + let v = #[hdl(sim)] + ty.V(0x23u8); + assert_eq!(format!("{v:?}"), "MyEnum1::V(0x23_u8)"); + let v = #[hdl(sim)] + ty.A([1u8, 2, 3]); + assert_eq!(format!("{v:?}"), "MyEnum1::A([0x1_u8, 0x2_u8, 0x3_u8])"); +} diff --git a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr index 6c78637..44aff3f 100644 --- a/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr +++ b/crates/fayalite/tests/ui/simvalue_is_not_internable.stderr @@ -75,12 +75,12 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 734 | pub struct OpaqueSimValue { + 761 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs | - 51 | struct SimValueInner { + 52 | struct SimValueInner { | ^^^^^^^^^^^^^ note: required because it appears within the type `UnsafeCell>` --> $RUST/core/src/cell.rs @@ -95,7 +95,7 @@ note: required because it appears within the type `util::alternating_cell::Alter note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | - 160 | pub struct SimValue { + 161 | pub struct SimValue { | ^^^^^^^^ note: required by a bound in `fayalite::intern::Interned` --> src/intern.rs @@ -214,12 +214,12 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 734 | pub struct OpaqueSimValue { + 761 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs | - 51 | struct SimValueInner { + 52 | struct SimValueInner { | ^^^^^^^^^^^^^ note: required because it appears within the type `UnsafeCell>` --> $RUST/core/src/cell.rs @@ -234,7 +234,7 @@ note: required because it appears within the type `util::alternating_cell::Alter note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | - 160 | pub struct SimValue { + 161 | pub struct SimValue { | ^^^^^^^^ note: required by a bound in `intern_sized` --> src/intern.rs @@ -326,12 +326,12 @@ note: required because it appears within the type `Vec` note: required because it appears within the type `OpaqueSimValue` --> src/ty.rs | - 734 | pub struct OpaqueSimValue { + 761 | pub struct OpaqueSimValue { | ^^^^^^^^^^^^^^ note: required because it appears within the type `value::SimValueInner<()>` --> src/sim/value.rs | - 51 | struct SimValueInner { + 52 | struct SimValueInner { | ^^^^^^^^^^^^^ note: required because it appears within the type `UnsafeCell>` --> $RUST/core/src/cell.rs @@ -346,7 +346,7 @@ note: required because it appears within the type `util::alternating_cell::Alter note: required because it appears within the type `fayalite::prelude::SimValue<()>` --> src/sim/value.rs | - 160 | pub struct SimValue { + 161 | pub struct SimValue { | ^^^^^^^^ note: required by a bound in `fayalite::intern::Interned` --> src/intern.rs