diff --git a/Cargo.lock b/Cargo.lock index 23cdc34..2e50abc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -543,9 +543,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.92" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -647,9 +647,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.93" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 54de3a8..2cfa586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ quote = "1.0.36" serde = { version = "1.0.202", features = ["derive"] } serde_json = { version = "1.0.117", features = ["preserve_order"] } sha2 = "0.10.8" -syn = { version = "2.0.93", features = ["full", "fold", "visit", "extra-traits"] } +syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] } tempfile = "3.10.1" thiserror = "1.0.61" trybuild = "1.0" diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 6ba177b..0ffd4d4 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -3,20 +3,14 @@ #![cfg_attr(test, recursion_limit = "512")] use proc_macro2::{Span, TokenStream}; use quote::{quote, ToTokens}; -use std::{ - collections::{hash_map::Entry, HashMap}, - io::{ErrorKind, Write}, -}; +use std::io::{ErrorKind, Write}; use syn::{ - bracketed, - ext::IdentExt, - parenthesized, + bracketed, parenthesized, parse::{Parse, ParseStream, Parser}, parse_quote, - punctuated::{Pair, Punctuated}, + punctuated::Pair, spanned::Spanned, - token::{Bracket, Paren}, - AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, + AttrStyle, Attribute, Error, Item, ItemFn, Token, }; mod fold; @@ -25,7 +19,6 @@ mod hdl_enum; mod hdl_type_alias; mod hdl_type_common; mod module; -mod process_cfg; pub(crate) trait CustomToken: Copy @@ -66,11 +59,6 @@ mod kw { }; } - custom_keyword!(__evaluated_cfgs); - custom_keyword!(all); - custom_keyword!(any); - custom_keyword!(cfg); - custom_keyword!(cfg_attr); custom_keyword!(clock_domain); custom_keyword!(connect_inexact); custom_keyword!(custom_bounds); @@ -87,7 +75,6 @@ mod kw { custom_keyword!(no_reset); custom_keyword!(no_runtime_generics); custom_keyword!(no_static); - custom_keyword!(not); custom_keyword!(outline_generated); custom_keyword!(output); custom_keyword!(reg_builder); @@ -914,346 +901,15 @@ fn hdl_module_impl(item: ItemFn) -> syn::Result { Ok(contents) } -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) enum CfgExpr { - Option { - ident: Ident, - value: Option<(Token![=], LitStr)>, - }, - All { - all: kw::all, - paren: Paren, - exprs: Punctuated, - }, - Any { - any: kw::any, - paren: Paren, - exprs: Punctuated, - }, - Not { - not: kw::not, - paren: Paren, - expr: Box, - trailing_comma: Option, - }, +pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { + let kw = kw::hdl_module::default(); + hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?) } -impl Parse for CfgExpr { - fn parse(input: ParseStream) -> syn::Result { - match input.cursor().ident() { - Some((_, cursor)) if cursor.eof() => { - return Ok(CfgExpr::Option { - ident: input.call(Ident::parse_any)?, - value: None, - }); - } - _ => {} - } - if input.peek(Ident::peek_any) && input.peek2(Token![=]) { - return Ok(CfgExpr::Option { - ident: input.call(Ident::parse_any)?, - value: Some((input.parse()?, input.parse()?)), - }); - } - let contents; - if input.peek(kw::all) { - Ok(CfgExpr::All { - all: input.parse()?, - paren: parenthesized!(contents in input), - exprs: contents.call(Punctuated::parse_terminated)?, - }) - } else if input.peek(kw::any) { - Ok(CfgExpr::Any { - any: input.parse()?, - paren: parenthesized!(contents in input), - exprs: contents.call(Punctuated::parse_terminated)?, - }) - } else if input.peek(kw::not) { - Ok(CfgExpr::Not { - not: input.parse()?, - paren: parenthesized!(contents in input), - expr: contents.parse()?, - trailing_comma: contents.parse()?, - }) - } else { - Err(input.error("expected cfg-pattern")) - } - } -} - -impl ToTokens for CfgExpr { - fn to_tokens(&self, tokens: &mut TokenStream) { - match self { - CfgExpr::Option { ident, value } => { - ident.to_tokens(tokens); - if let Some((eq, value)) = value { - eq.to_tokens(tokens); - value.to_tokens(tokens); - } - } - CfgExpr::All { all, paren, exprs } => { - all.to_tokens(tokens); - paren.surround(tokens, |tokens| exprs.to_tokens(tokens)); - } - CfgExpr::Any { any, paren, exprs } => { - any.to_tokens(tokens); - paren.surround(tokens, |tokens| exprs.to_tokens(tokens)); - } - CfgExpr::Not { - not, - paren, - expr, - trailing_comma, - } => { - not.to_tokens(tokens); - paren.surround(tokens, |tokens| { - expr.to_tokens(tokens); - trailing_comma.to_tokens(tokens); - }); - } - } - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct Cfg { - cfg: kw::cfg, - paren: Paren, - expr: CfgExpr, - trailing_comma: Option, -} - -impl Cfg { - fn parse_meta(meta: &Meta) -> syn::Result { - syn::parse2(meta.to_token_stream()) - } -} - -impl ToTokens for Cfg { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - cfg, - paren, - expr, - trailing_comma, - } = self; - cfg.to_tokens(tokens); - paren.surround(tokens, |tokens| { - expr.to_tokens(tokens); - trailing_comma.to_tokens(tokens); - }); - } -} - -impl Parse for Cfg { - fn parse(input: ParseStream) -> syn::Result { - let contents; - Ok(Self { - cfg: input.parse()?, - paren: parenthesized!(contents in input), - expr: contents.parse()?, - trailing_comma: contents.parse()?, - }) - } -} - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub(crate) struct CfgAttr { - cfg_attr: kw::cfg_attr, - paren: Paren, - expr: CfgExpr, - comma: Token![,], - attrs: Punctuated, -} - -impl CfgAttr { - pub(crate) fn to_cfg(&self) -> Cfg { - Cfg { - cfg: kw::cfg(self.cfg_attr.span), - paren: self.paren, - expr: self.expr.clone(), - trailing_comma: None, - } - } - fn parse_meta(meta: &Meta) -> syn::Result { - syn::parse2(meta.to_token_stream()) - } -} - -impl Parse for CfgAttr { - fn parse(input: ParseStream) -> syn::Result { - let contents; - Ok(Self { - cfg_attr: input.parse()?, - paren: parenthesized!(contents in input), - expr: contents.parse()?, - comma: contents.parse()?, - attrs: contents.call(Punctuated::parse_terminated)?, - }) - } -} - -pub(crate) struct CfgAndValue { - cfg: Cfg, - eq_token: Token![=], - value: LitBool, -} - -impl Parse for CfgAndValue { - fn parse(input: ParseStream) -> syn::Result { - Ok(Self { - cfg: input.parse()?, - eq_token: input.parse()?, - value: input.parse()?, - }) - } -} - -pub(crate) struct Cfgs { - pub(crate) bracket: Bracket, - pub(crate) cfgs_map: HashMap, - pub(crate) cfgs_list: Vec, -} - -impl Default for Cfgs { - fn default() -> Self { - Self { - bracket: Default::default(), - cfgs_map: Default::default(), - cfgs_list: Default::default(), - } - } -} - -impl Cfgs { - fn insert_cfg(&mut self, cfg: Cfg, value: T) { - match self.cfgs_map.entry(cfg) { - Entry::Occupied(_) => {} - Entry::Vacant(entry) => { - self.cfgs_list.push(entry.key().clone()); - entry.insert(value); - } - } - } -} - -impl Parse for Cfgs { - fn parse(input: ParseStream) -> syn::Result { - let contents; - let bracket = bracketed!(contents in input); - let mut cfgs_map = HashMap::new(); - let mut cfgs_list = Vec::new(); - for CfgAndValue { - cfg, - eq_token, - value, - } in contents.call(Punctuated::::parse_terminated)? - { - let _ = eq_token; - match cfgs_map.entry(cfg) { - Entry::Occupied(_) => {} - Entry::Vacant(entry) => { - cfgs_list.push(entry.key().clone()); - entry.insert(value.value); - } - } - } - Ok(Self { - bracket, - cfgs_map, - cfgs_list, - }) - } -} - -impl Parse for Cfgs<()> { - fn parse(input: ParseStream) -> syn::Result { - let contents; - let bracket = bracketed!(contents in input); - let mut cfgs_map = HashMap::new(); - let mut cfgs_list = Vec::new(); - for cfg in contents.call(Punctuated::::parse_terminated)? { - match cfgs_map.entry(cfg) { - Entry::Occupied(_) => {} - Entry::Vacant(entry) => { - cfgs_list.push(entry.key().clone()); - entry.insert(()); - } - } - } - Ok(Self { - bracket, - cfgs_map, - cfgs_list, - }) - } -} - -impl ToTokens for Cfgs<()> { - fn to_tokens(&self, tokens: &mut TokenStream) { - let Self { - bracket, - cfgs_map: _, - cfgs_list, - } = self; - bracket.surround(tokens, |tokens| { - for cfg in cfgs_list { - cfg.to_tokens(tokens); - ::default().to_tokens(tokens); - } - }); - } -} - -fn hdl_main( - kw: impl CustomToken, - attr: TokenStream, - item: TokenStream, -) -> syn::Result { - fn parse_evaluated_cfgs_attr( - input: ParseStream, - parse_inner: impl FnOnce(ParseStream) -> syn::Result, - ) -> syn::Result { - let _: Token![#] = input.parse()?; - let bracket_content; - bracketed!(bracket_content in input); - let _: kw::__evaluated_cfgs = bracket_content.parse()?; - let paren_content; - parenthesized!(paren_content in bracket_content); - parse_inner(&paren_content) - } - let (evaluated_cfgs, item): (_, TokenStream) = Parser::parse2( - |input: ParseStream| { - let peek = input.fork(); - if parse_evaluated_cfgs_attr(&peek, |_| Ok(())).is_ok() { - let evaluated_cfgs = parse_evaluated_cfgs_attr(input, Cfgs::::parse)?; - Ok((Some(evaluated_cfgs), input.parse()?)) - } else { - Ok((None, input.parse()?)) - } - }, - item, - )?; - let cfgs = if let Some(cfgs) = evaluated_cfgs { - cfgs - } else { - let cfgs = process_cfg::collect_cfgs(syn::parse2(item.clone())?)?; - if cfgs.cfgs_list.is_empty() { - Cfgs::default() - } else { - return Ok(quote! { - ::fayalite::__cfg_expansion_helper! { - [] - #cfgs - {#[::fayalite::#kw(#attr)]} { #item } - } - }); - } - }; - let item = syn::parse2(quote! { #[#kw(#attr)] #item })?; - let Some(item) = process_cfg::process_cfgs(item, cfgs)? else { - return Ok(TokenStream::new()); - }; +pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result { + let kw = kw::hdl::default(); + let item = quote! { #[#kw(#attr)] #item }; + let item = syn::parse2::(item)?; match item { Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item), @@ -1265,11 +921,3 @@ fn hdl_main( )), } } - -pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { - hdl_main(kw::hdl_module::default(), attr, item) -} - -pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result { - hdl_main(kw::hdl::default(), attr, item) -} diff --git a/crates/fayalite-proc-macros-impl/src/process_cfg.rs b/crates/fayalite-proc-macros-impl/src/process_cfg.rs deleted file mode 100644 index 5cff08f..0000000 --- a/crates/fayalite-proc-macros-impl/src/process_cfg.rs +++ /dev/null @@ -1,2527 +0,0 @@ -// SPDX-License-Identifier: LGPL-3.0-or-later -// See Notices.txt for copyright information - -use crate::{Cfg, CfgAttr, Cfgs, Errors}; -use proc_macro2::Ident; -use std::{collections::VecDeque, marker::PhantomData}; -use syn::{ - punctuated::{Pair, Punctuated}, - Token, -}; - -struct State { - cfgs: Cfgs, - errors: Errors, - _phantom: PhantomData

, -} - -impl State

{ - #[must_use] - fn eval_cfg(&mut self, cfg: Cfg) -> bool { - struct MyDispatch<'a> { - cfg: Cfg, - _phantom: PhantomData<&'a ()>, - } - impl<'a> PhaseDispatch for MyDispatch<'a> { - type Args = &'a mut State

; - type Output = bool; - - fn dispatch_collect( - self, - args: Self::Args, - ) -> Self::Output { - args.cfgs.insert_cfg(self.cfg, ()); - true - } - - fn dispatch_process( - self, - args: Self::Args, - ) -> Self::Output { - if let Some(&retval) = args.cfgs.cfgs_map.get(&self.cfg) { - retval - } else { - args.errors.error(self.cfg, "unrecognized cfg -- cfg wasn't evaluated when running `__cfg_expansion_helper!`"); - true - } - } - } - P::dispatch( - MyDispatch { - cfg, - _phantom: PhantomData, - }, - self, - ) - } - #[must_use] - fn eval_cfgs( - &mut self, - mut attrs: Vec, - ) -> Option, P>> { - let mut queue = VecDeque::from(attrs); - attrs = Vec::with_capacity(queue.len()); // cfg_attr is rare, and cfg can't increase length - while let Some(attr) = queue.pop_front() { - if attr.path().is_ident("cfg") { - if let Some(cfg) = self.errors.ok(Cfg::parse_meta(&attr.meta)) { - if !self.eval_cfg(cfg) { - return None; - } - continue; - } - } else if attr.path().is_ident("cfg_attr") { - if let Some(cfg_attr) = self.errors.ok(CfgAttr::parse_meta(&attr.meta)) { - if self.eval_cfg(cfg_attr.to_cfg()) { - // push onto queue since cfg_attr(, cfg_attr(, )) is valid - for meta in cfg_attr.attrs { - queue.push_front(syn::Attribute { - pound_token: attr.pound_token, - style: attr.style, - bracket_token: attr.bracket_token, - meta, - }); - } - } - continue; - } - } - attrs.push(attr); - } - Some(Output::new(attrs)) - } - fn process_qself_and_path( - &mut self, - qself: Option, - path: syn::Path, - ) -> Option<(Output, P>, Output)> { - let qself = if let Some(syn::QSelf { - lt_token, - ty, - position, - as_token, - gt_token, - }) = qself - { - ty.process(self)?.map(|ty| { - Some(syn::QSelf { - lt_token, - ty, - position, - as_token, - gt_token, - }) - }) - } else { - Output::new(None) - }; - let syn::Path { - leading_colon, - segments, - } = path; - // path segments don't get removed - let path = segments.process(self)?.map(|segments| syn::Path { - leading_colon, - segments, - }); - Some((qself, path)) - } -} - -trait PhaseDispatch { - type Args; - type Output; - fn dispatch_collect(self, args: Self::Args) - -> Self::Output; - fn dispatch_process(self, args: Self::Args) - -> Self::Output; -} - -trait Phase: Sized + 'static { - type Output; - type CfgsValue; - fn output_new(v: T) -> Output; - fn output_map U>(v: Output, f: F) -> Output; - fn output_zip(t: Output, u: Output) -> Output<(T, U), Self>; - fn dispatch(d: D, args: D::Args) -> D::Output; -} - -struct CollectCfgsPhase; - -impl Phase for CollectCfgsPhase { - type Output = (); - type CfgsValue = (); - - fn output_new(_v: T) -> Output { - Output(()) - } - - fn output_map U>(_v: Output, _f: F) -> Output { - Output(()) - } - - fn output_zip(_t: Output, _u: Output) -> Output<(T, U), Self> { - Output(()) - } - - fn dispatch(d: D, args: D::Args) -> D::Output { - d.dispatch_collect(args) - } -} - -struct ProcessCfgsPhase; - -impl Phase for ProcessCfgsPhase { - type Output = T; - type CfgsValue = bool; - - fn output_new(v: T) -> Output { - Output(v) - } - - fn output_map U>(v: Output, f: F) -> Output { - Output(f(v.0)) - } - - fn output_zip(t: Output, u: Output) -> Output<(T, U), Self> { - Output((t.0, u.0)) - } - - fn dispatch(d: D, args: D::Args) -> D::Output { - d.dispatch_process(args) - } -} - -struct Output(P::Output); - -trait OutputZip: Sized { - type Output; - fn zip(self) -> Output; - fn call R>(self, f: F) -> Output { - self.zip().map(f) - } -} - -impl OutputZip

for () { - type Output = (); - - fn zip(self) -> Output { - Output::new(()) - } -} - -impl OutputZip

for (Output,) { - type Output = (T,); - - fn zip(self) -> Output { - self.0.map(|v| (v,)) - } -} - -macro_rules! impl_zip { - ($first_arg:ident: $first_T:ident, $($arg:ident: $T:ident),* $(,)?) => { - impl_zip!(@step [], [($first_arg: $first_T) $(($arg: $T))*], (),); - }; - ( - @impl($first_arg:tt,), - $tuple_pat:tt, - ) => {}; - ( - @impl(($first_arg:ident: $first_T:ident), - $(($arg:ident: $T:ident),)*), - $tuple_pat:tt, - ) => { - impl<$first_T, $($T,)* P: Phase> OutputZip

for (Output<$first_T, P>, $(Output<$T, P>),*) { - type Output = ($first_T, $($T),*); - fn zip(self) -> Output<($first_T, $($T),*), P> { - let (tuples, $($arg),*) = self; - $(let tuples = P::output_zip(tuples, $arg);)* - tuples.map(|$tuple_pat| ($first_arg, $($arg),*)) - } - } - }; - ( - @step [$($cur:tt)*], - [], - $tuple_pat:tt, - ) => {}; - ( - @step [$($cur:tt)*], - [($next_arg:ident: $next_T:ident) $($rest:tt)*], - (), - ) => { - impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), $next_arg,); - impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], $next_arg,); - }; - ( - @step [$($cur:tt)*], - [($next_arg:ident: $next_T:ident) $($rest:tt)*], - $tuple_pat:tt, - ) => { - impl_zip!(@impl($($cur,)* ($next_arg: $next_T),), ($tuple_pat, $next_arg),); - impl_zip!(@step [$($cur)* ($next_arg: $next_T)], [$($rest)*], ($tuple_pat, $next_arg),); - }; -} - -impl_zip!(t0: T0, t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8, t9: T9, t10: T10, t11: T11); - -impl Copy for Output where P::Output: Copy {} - -impl Clone for Output -where - P::Output: Clone, -{ - fn clone(&self) -> Self { - Self(self.0.clone()) - } -} - -impl Output { - fn new(v: T) -> Self { - P::output_new(v) - } - fn map U>(self, f: F) -> Output { - P::output_map(self, f) - } -} - -trait Process: Sized { - #[must_use] - fn process(self, state: &mut State

) -> Option>; -} - -impl Process

for syn::Item { - fn process(self, _state: &mut State

) -> Option> { - // don't recurse into items - Some(Output::new(self)) - } -} - -impl Process

for Vec { - fn process(self, state: &mut State

) -> Option> { - state.eval_cfgs(self) - } -} - -impl, P: Phase> Process

for Box { - fn process(self, state: &mut State

) -> Option> { - Some(T::process(*self, state)?.map(Box::new)) - } -} - -trait ProcessVecElement { - const REMOVE_ELEMENTS: bool; -} - -impl ProcessVecElement for syn::Arm { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessVecElement for syn::Stmt { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessVecElement for syn::ForeignItem { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessVecElement for syn::ImplItem { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessVecElement for syn::Item { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessVecElement for syn::TraitItem { - const REMOVE_ELEMENTS: bool = true; -} - -impl + ProcessVecElement, P: Phase> Process

for Vec { - fn process(self, state: &mut State

) -> Option> { - let mut output = Output::new(Vec::new()); - for value in self { - if let Some(value) = value.process(state) { - output = (output, value).call(|(mut output, value)| { - output.push(value); - output - }); - } else if !T::REMOVE_ELEMENTS { - return None; - } - } - Some(output) - } -} - -trait ProcessOption { - /// if a configured-off value causes this value to be `None` instead of propagating the configuring-off - const REMOVE_VALUE: bool; -} - -impl ProcessOption for syn::Abi { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::Block { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::WhereClause { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::Expr { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::Type { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for Box { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::AngleBracketedGenericArguments { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::ImplRestriction { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for syn::BoundLifetimes { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for (Token![=], syn::Expr) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Token![=], syn::Type) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Token![if], Box) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Token![else], Box) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Token![&], Option) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Token![as], Ident) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Ident, Token![:]) { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Option, syn::Path, Token![for]) { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for syn::BareVariadic { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::Variadic { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::LocalInit { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for syn::Label { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for syn::PatRest { - const REMOVE_VALUE: bool = true; -} - -impl ProcessOption for (Box, Token![:]) { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for (Token![@], Box) { - const REMOVE_VALUE: bool = false; -} - -impl ProcessOption for (syn::token::Brace, Vec) { - const REMOVE_VALUE: bool = false; -} - -impl + ProcessOption, P: Phase> Process

for Option { - fn process(self, state: &mut State

) -> Option> { - if let Some(this) = self { - match this.process(state) { - Some(v) => Some(v.map(Some)), - None => { - if T::REMOVE_VALUE { - Some(Output::new(None)) - } else { - None - } - } - } - } else { - Some(Output::new(None)) - } - } -} - -trait ProcessPunctuatedElement { - const REMOVE_ELEMENTS: bool; -} - -impl + ProcessPunctuatedElement, P: Phase, Punct: Default> Process

- for Punctuated -{ - fn process(self, state: &mut State

) -> Option> { - let mut output = Output::new(Punctuated::::new()); - for pair in self.into_pairs() { - let (value, punct) = pair.into_tuple(); - if let Some(value) = value.process(state) { - output = (output, value).call(|(mut output, value)| { - output.extend([Pair::new(value, punct)]); - output - }); - } else if !T::REMOVE_ELEMENTS { - return None; - } - } - Some(output) - } -} - -impl ProcessPunctuatedElement for syn::PathSegment { - const REMOVE_ELEMENTS: bool = false; -} - -impl ProcessPunctuatedElement for syn::Type { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::Expr { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::Pat { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::CapturedParam { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::GenericArgument { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::GenericParam { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::Lifetime { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::WherePredicate { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::Variant { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::FnArg { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::BareFnArg { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::TypeParamBound { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::FieldValue { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::Field { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::FieldPat { - const REMOVE_ELEMENTS: bool = true; -} - -impl ProcessPunctuatedElement for syn::UseTree { - const REMOVE_ELEMENTS: bool = true; -} - -impl, U: Process

, P: Phase> Process

for (T, U) { - fn process(self, state: &mut State

) -> Option> { - let (t, u) = self; - let t = t.process(state)?; - let u = u.process(state)?; - Some((t, u).zip()) - } -} - -impl, U: Process

, V: Process

, P: Phase> Process

for (T, U, V) { - fn process(self, state: &mut State

) -> Option> { - let (t, u, v) = self; - let t = t.process(state)?; - let u = u.process(state)?; - let v = v.process(state)?; - Some((t, u, v).zip()) - } -} - -macro_rules! process_no_op { - ($ty:ty) => { - impl Process

for $ty { - fn process(self, _state: &mut State

) -> Option> { - Some(Output::new(self)) - } - } - - impl ProcessOption for $ty { - const REMOVE_VALUE: bool = false; - } - }; -} - -process_no_op!(Token![Self]); -process_no_op!(Token![abstract]); -process_no_op!(Token![as]); -process_no_op!(Token![async]); -process_no_op!(Token![auto]); -process_no_op!(Token![await]); -process_no_op!(Token![become]); -process_no_op!(Token![box]); -process_no_op!(Token![break]); -process_no_op!(Token![const]); -process_no_op!(Token![continue]); -process_no_op!(Token![crate]); -process_no_op!(Token![default]); -process_no_op!(Token![do]); -process_no_op!(Token![dyn]); -process_no_op!(Token![else]); -process_no_op!(Token![enum]); -process_no_op!(Token![extern]); -process_no_op!(Token![final]); -process_no_op!(Token![fn]); -process_no_op!(Token![for]); -process_no_op!(Token![if]); -process_no_op!(Token![impl]); -process_no_op!(Token![in]); -process_no_op!(Token![let]); -process_no_op!(Token![loop]); -process_no_op!(Token![macro]); -process_no_op!(Token![match]); -process_no_op!(Token![mod]); -process_no_op!(Token![move]); -process_no_op!(Token![mut]); -process_no_op!(Token![override]); -process_no_op!(Token![priv]); -process_no_op!(Token![pub]); -process_no_op!(Token![raw]); -process_no_op!(Token![ref]); -process_no_op!(Token![return]); -process_no_op!(Token![self]); -process_no_op!(Token![static]); -process_no_op!(Token![struct]); -process_no_op!(Token![super]); -process_no_op!(Token![trait]); -process_no_op!(Token![try]); -process_no_op!(Token![type]); -process_no_op!(Token![typeof]); -process_no_op!(Token![union]); -process_no_op!(Token![unsafe]); -process_no_op!(Token![unsized]); -process_no_op!(Token![use]); -process_no_op!(Token![virtual]); -process_no_op!(Token![where]); -process_no_op!(Token![while]); -process_no_op!(Token![yield]); - -process_no_op!(Token![!]); -process_no_op!(Token![!=]); -process_no_op!(Token![#]); -process_no_op!(Token![$]); -process_no_op!(Token![%]); -process_no_op!(Token![%=]); -process_no_op!(Token![&]); -process_no_op!(Token![&&]); -process_no_op!(Token![&=]); -process_no_op!(Token![*]); -process_no_op!(Token![*=]); -process_no_op!(Token![+]); -process_no_op!(Token![+=]); -process_no_op!(Token![,]); -process_no_op!(Token![-]); -process_no_op!(Token![-=]); -process_no_op!(Token![->]); -process_no_op!(Token![.]); -process_no_op!(Token![..]); -process_no_op!(Token![...]); -process_no_op!(Token![..=]); -process_no_op!(Token![/]); -process_no_op!(Token![/=]); -process_no_op!(Token![:]); -process_no_op!(Token![::]); -process_no_op!(Token![;]); -process_no_op!(Token![<]); -process_no_op!(Token![<-]); -process_no_op!(Token![<<]); -process_no_op!(Token![<<=]); -process_no_op!(Token![<=]); -process_no_op!(Token![=]); -process_no_op!(Token![==]); -process_no_op!(Token![=>]); -process_no_op!(Token![>]); -process_no_op!(Token![>=]); -process_no_op!(Token![>>]); -process_no_op!(Token![>>=]); -process_no_op!(Token![?]); -process_no_op!(Token![@]); -process_no_op!(Token![^]); -process_no_op!(Token![^=]); -process_no_op!(Token![_]); -process_no_op!(Token![|]); -process_no_op!(Token![|=]); -process_no_op!(Token![||]); -process_no_op!(Token![~]); - -process_no_op!(syn::token::Brace); -process_no_op!(syn::token::Bracket); -process_no_op!(syn::token::Paren); -process_no_op!(syn::token::Group); - -process_no_op!(Ident); -process_no_op!(syn::Index); -process_no_op!(syn::Lifetime); -process_no_op!(syn::LitBool); -process_no_op!(syn::LitByte); -process_no_op!(syn::LitByteStr); -process_no_op!(syn::LitChar); -process_no_op!(syn::LitCStr); -process_no_op!(syn::LitFloat); -process_no_op!(syn::LitInt); -process_no_op!(syn::LitStr); -process_no_op!(proc_macro2::TokenStream); -process_no_op!(proc_macro2::Literal); - -macro_rules! process_struct { - ($ty:path { - $($field:ident,)* - }) => { - impl Process

for $ty { - fn process(self, state: &mut State

) -> Option> { - let Self { - $($field,)* - } = self; - $(let $field = $field.process(state)?;)* - Some(($($field,)*).call(|($($field,)*)| Self { - $($field,)* - })) - } - } - }; - ($ty:path { - $($fields_before:ident,)* - #[qself] - $qself:ident, - $path:ident, - $($fields_after:ident,)* - }) => { - impl Process

for $ty { - fn process(self, state: &mut State

) -> Option> { - let Self { - $($fields_before,)* - $qself, - $path, - $($fields_after,)* - } = self; - $(let $fields_before = $fields_before.process(state)?;)* - let ($qself, $path) = state.process_qself_and_path($qself, $path)?; - $(let $fields_after = $fields_after.process(state)?;)* - Some(( - $($fields_before,)* - $qself, - $path, - $($fields_after,)* - ).call(|( - $($fields_before,)* - $qself, - $path, - $($fields_after,)* - )| Self { - $($fields_before,)* - $qself, - $path, - $($fields_after,)* - })) - } - } - }; -} - -process_struct! { - syn::Abi { - extern_token, - name, - } -} - -process_struct! { - syn::AngleBracketedGenericArguments { - colon2_token, - lt_token, - args, - gt_token, - } -} - -process_struct! { - syn::Arm { - attrs, - pat, - guard, - fat_arrow_token, - body, - comma, - } -} - -process_struct! { - syn::AssocConst { - ident, - generics, - eq_token, - value, - } -} - -process_struct! { - syn::AssocType { - ident, - generics, - eq_token, - ty, - } -} - -process_struct! { - syn::BareFnArg { - attrs, - name, - ty, - } -} - -process_struct! { - syn::BareVariadic { - attrs, - name, - dots, - comma, - } -} - -process_struct! { - syn::Block { - brace_token, - stmts, - } -} - -process_struct! { - syn::BoundLifetimes { - for_token, - lt_token, - lifetimes, - gt_token, - } -} - -process_struct! { - syn::ConstParam { - attrs, - const_token, - ident, - colon_token, - ty, - eq_token, - default, - } -} - -process_struct! { - syn::Constraint { - ident, - generics, - colon_token, - bounds, - } -} - -process_struct! { - syn::DataEnum { - enum_token, - brace_token, - variants, - } -} - -process_struct! { - syn::DataStruct { - struct_token, - fields, - semi_token, - } -} - -process_struct! { - syn::DataUnion { - union_token, - fields, - } -} - -process_struct! { - syn::DeriveInput { - attrs, - vis, - ident, - generics, - data, - } -} - -process_struct! { - syn::ExprArray { - attrs, - bracket_token, - elems, - } -} - -process_struct! { - syn::ExprAssign { - attrs, - left, - eq_token, - right, - } -} - -process_struct! { - syn::ExprAsync { - attrs, - async_token, - capture, - block, - } -} - -process_struct! { - syn::ExprAwait { - attrs, - base, - dot_token, - await_token, - } -} - -process_struct! { - syn::ExprBinary { - attrs, - left, - op, - right, - } -} - -process_struct! { - syn::ExprBlock { - attrs, - label, - block, - } -} - -process_struct! { - syn::ExprBreak { - attrs, - break_token, - label, - expr, - } -} - -process_struct! { - syn::ExprCall { - attrs, - func, - paren_token, - args, - } -} - -process_struct! { - syn::ExprCast { - attrs, - expr, - as_token, - ty, - } -} - -process_struct! { - syn::ExprClosure { - attrs, - lifetimes, - constness, - movability, - asyncness, - capture, - or1_token, - inputs, - or2_token, - output, - body, - } -} - -process_struct! { - syn::ExprConst { - attrs, - const_token, - block, - } -} - -process_struct! { - syn::ExprContinue { - attrs, - continue_token, - label, - } -} - -process_struct! { - syn::ExprField { - attrs, - base, - dot_token, - member, - } -} - -process_struct! { - syn::ExprForLoop { - attrs, - label, - for_token, - pat, - in_token, - expr, - body, - } -} - -process_struct! { - syn::ExprGroup { - attrs, - group_token, - expr, - } -} - -process_struct! { - syn::ExprIf { - attrs, - if_token, - cond, - then_branch, - else_branch, - } -} - -process_struct! { - syn::ExprIndex { - attrs, - expr, - bracket_token, - index, - } -} - -process_struct! { - syn::ExprInfer { - attrs, - underscore_token, - } -} - -process_struct! { - syn::ExprLet { - attrs, - let_token, - pat, - eq_token, - expr, - } -} - -process_struct! { - syn::ExprLit { - attrs, - lit, - } -} - -process_struct! { - syn::ExprLoop { - attrs, - label, - loop_token, - body, - } -} - -process_struct! { - syn::ExprMacro { - attrs, - mac, - } -} - -process_struct! { - syn::ExprMatch { - attrs, - match_token, - expr, - brace_token, - arms, - } -} - -process_struct! { - syn::ExprMethodCall { - attrs, - receiver, - dot_token, - method, - turbofish, - paren_token, - args, - } -} - -process_struct! { - syn::ExprParen { - attrs, - paren_token, - expr, - } -} - -process_struct! { - syn::ExprPath { - attrs, - #[qself] - qself, - path, - } -} - -process_struct! { - syn::ExprRange { - attrs, - start, - limits, - end, - } -} - -process_struct! { - syn::ExprRawAddr { - attrs, - and_token, - raw, - mutability, - expr, - } -} - -process_struct! { - syn::ExprReference { - attrs, - and_token, - mutability, - expr, - } -} - -process_struct! { - syn::ExprRepeat { - attrs, - bracket_token, - expr, - semi_token, - len, - } -} - -process_struct! { - syn::ExprReturn { - attrs, - return_token, - expr, - } -} - -process_struct! { - syn::ExprStruct { - attrs, - #[qself] - qself, - path, - brace_token, - fields, - dot2_token, - rest, - } -} - -process_struct! { - syn::ExprTry { - attrs, - expr, - question_token, - } -} - -process_struct! { - syn::ExprTryBlock { - attrs, - try_token, - block, - } -} - -process_struct! { - syn::ExprTuple { - attrs, - paren_token, - elems, - } -} - -process_struct! { - syn::ExprUnary { - attrs, - op, - expr, - } -} - -process_struct! { - syn::ExprUnsafe { - attrs, - unsafe_token, - block, - } -} - -process_struct! { - syn::ExprWhile { - attrs, - label, - while_token, - cond, - body, - } -} - -process_struct! { - syn::ExprYield { - attrs, - yield_token, - expr, - } -} - -process_struct! { - syn::Field { - attrs, - vis, - mutability, - ident, - colon_token, - ty, - } -} - -process_struct! { - syn::FieldPat { - attrs, - member, - colon_token, - pat, - } -} - -process_struct! { - syn::FieldValue { - attrs, - member, - colon_token, - expr, - } -} - -process_struct! { - syn::FieldsNamed { - brace_token, - named, - } -} - -process_struct! { - syn::FieldsUnnamed { - paren_token, - unnamed, - } -} - -process_struct! { - syn::ForeignItemFn { - attrs, - vis, - sig, - semi_token, - } -} - -process_struct! { - syn::ForeignItemMacro { - attrs, - mac, - semi_token, - } -} - -process_struct! { - syn::ForeignItemStatic { - attrs, - vis, - static_token, - mutability, - ident, - colon_token, - ty, - semi_token, - } -} - -process_struct! { - syn::ForeignItemType { - attrs, - vis, - type_token, - ident, - generics, - semi_token, - } -} - -process_struct! { - syn::Generics { - lt_token, - params, - gt_token, - where_clause, - } -} - -process_struct! { - syn::ImplItemConst { - attrs, - vis, - defaultness, - const_token, - ident, - generics, - colon_token, - ty, - eq_token, - expr, - semi_token, - } -} - -process_struct! { - syn::ImplItemFn { - attrs, - vis, - defaultness, - sig, - block, - } -} - -process_struct! { - syn::ImplItemMacro { - attrs, - mac, - semi_token, - } -} - -process_struct! { - syn::ImplItemType { - attrs, - vis, - defaultness, - type_token, - ident, - generics, - eq_token, - ty, - semi_token, - } -} - -process_struct! { - syn::ItemConst { - attrs, - vis, - const_token, - ident, - generics, - colon_token, - ty, - eq_token, - expr, - semi_token, - } -} - -process_struct! { - syn::ItemEnum { - attrs, - vis, - enum_token, - ident, - generics, - brace_token, - variants, - } -} - -process_struct! { - syn::ItemExternCrate { - attrs, - vis, - extern_token, - crate_token, - ident, - rename, - semi_token, - } -} - -process_struct! { - syn::ItemFn { - attrs, - vis, - sig, - block, - } -} - -process_struct! { - syn::ItemForeignMod { - attrs, - unsafety, - abi, - brace_token, - items, - } -} - -process_struct! { - syn::ItemImpl { - attrs, - defaultness, - unsafety, - impl_token, - generics, - trait_, - self_ty, - brace_token, - items, - } -} - -process_struct! { - syn::ItemMacro { - attrs, - ident, - mac, - semi_token, - } -} - -process_struct! { - syn::ItemMod { - attrs, - vis, - unsafety, - mod_token, - ident, - content, - semi, - } -} - -process_struct! { - syn::ItemStatic { - attrs, - vis, - static_token, - mutability, - ident, - colon_token, - ty, - eq_token, - expr, - semi_token, - } -} - -process_struct! { - syn::ItemStruct { - attrs, - vis, - struct_token, - ident, - generics, - fields, - semi_token, - } -} - -process_struct! { - syn::ItemTrait { - attrs, - vis, - unsafety, - auto_token, - restriction, - trait_token, - ident, - generics, - colon_token, - supertraits, - brace_token, - items, - } -} - -process_struct! { - syn::ItemTraitAlias { - attrs, - vis, - trait_token, - ident, - generics, - eq_token, - bounds, - semi_token, - } -} - -process_struct! { - syn::ItemType { - attrs, - vis, - type_token, - ident, - generics, - eq_token, - ty, - semi_token, - } -} - -process_struct! { - syn::ItemUnion { - attrs, - vis, - union_token, - ident, - generics, - fields, - } -} - -process_struct! { - syn::ItemUse { - attrs, - vis, - use_token, - leading_colon, - tree, - semi_token, - } -} - -process_struct! { - syn::Label { - name, - colon_token, - } -} - -process_struct! { - syn::LifetimeParam { - attrs, - lifetime, - colon_token, - bounds, - } -} - -process_struct! { - syn::Local { - attrs, - let_token, - pat, - init, - semi_token, - } -} - -process_struct! { - syn::LocalInit { - eq_token, - expr, - diverge, - } -} - -process_struct! { - syn::Macro { - path, - bang_token, - delimiter, - tokens, - } -} - -process_struct! { - syn::MetaList { - path, - delimiter, - tokens, - } -} - -process_struct! { - syn::MetaNameValue { - path, - eq_token, - value, - } -} - -process_struct! { - syn::ParenthesizedGenericArguments { - paren_token, - inputs, - output, - } -} - -process_struct! { - syn::PatIdent { - attrs, - by_ref, - mutability, - ident, - subpat, - } -} - -process_struct! { - syn::PatOr { - attrs, - leading_vert, - cases, - } -} - -process_struct! { - syn::PatParen { - attrs, - paren_token, - pat, - } -} - -process_struct! { - syn::PatReference { - attrs, - and_token, - mutability, - pat, - } -} - -process_struct! { - syn::PatRest { - attrs, - dot2_token, - } -} - -process_struct! { - syn::PatSlice { - attrs, - bracket_token, - elems, - } -} - -process_struct! { - syn::PatStruct { - attrs, - #[qself] - qself, - path, - brace_token, - fields, - rest, - } -} - -process_struct! { - syn::PatTuple { - attrs, - paren_token, - elems, - } -} - -process_struct! { - syn::PatTupleStruct { - attrs, - #[qself] - qself, - path, - paren_token, - elems, - } -} - -process_struct! { - syn::PatType { - attrs, - pat, - colon_token, - ty, - } -} - -process_struct! { - syn::PatWild { - attrs, - underscore_token, - } -} - -process_struct! { - syn::Path { - leading_colon, - segments, - } -} - -process_struct! { - syn::PathSegment { - ident, - arguments, - } -} - -process_struct! { - syn::PreciseCapture { - use_token, - lt_token, - params, - gt_token, - } -} - -process_struct! { - syn::PredicateLifetime { - lifetime, - colon_token, - bounds, - } -} - -process_struct! { - syn::PredicateType { - lifetimes, - bounded_ty, - colon_token, - bounds, - } -} - -process_struct! { - syn::Receiver { - attrs, - reference, - mutability, - self_token, - colon_token, - ty, - } -} - -process_struct! { - syn::Signature { - constness, - asyncness, - unsafety, - abi, - fn_token, - ident, - generics, - paren_token, - inputs, - variadic, - output, - } -} - -process_struct! { - syn::StmtMacro { - attrs, - mac, - semi_token, - } -} - -process_struct! { - syn::TraitBound { - paren_token, - modifier, - lifetimes, - path, - } -} - -process_struct! { - syn::TraitItemConst { - attrs, - const_token, - ident, - generics, - colon_token, - ty, - default, - semi_token, - } -} - -process_struct! { - syn::TraitItemFn { - attrs, - sig, - default, - semi_token, - } -} - -process_struct! { - syn::TraitItemMacro { - attrs, - mac, - semi_token, - } -} - -process_struct! { - syn::TraitItemType { - attrs, - type_token, - ident, - generics, - colon_token, - bounds, - default, - semi_token, - } -} - -process_struct! { - syn::TypeArray { - bracket_token, - elem, - semi_token, - len, - } -} - -process_struct! { - syn::TypeBareFn { - lifetimes, - unsafety, - abi, - fn_token, - paren_token, - inputs, - variadic, - output, - } -} - -process_struct! { - syn::TypeGroup { - group_token, - elem, - } -} - -process_struct! { - syn::TypeImplTrait { - impl_token, - bounds, - } -} - -process_struct! { - syn::TypeInfer { - underscore_token, - } -} - -process_struct! { - syn::TypeMacro { - mac, - } -} - -process_struct! { - syn::TypeNever { - bang_token, - } -} - -process_struct! { - syn::TypeParam { - attrs, - ident, - colon_token, - bounds, - eq_token, - default, - } -} - -process_struct! { - syn::TypeParen { - paren_token, - elem, - } -} - -process_struct! { - syn::TypePath { - #[qself] - qself, - path, - } -} - -process_struct! { - syn::TypePtr { - star_token, - const_token, - mutability, - elem, - } -} - -process_struct! { - syn::TypeReference { - and_token, - lifetime, - mutability, - elem, - } -} - -process_struct! { - syn::TypeSlice { - bracket_token, - elem, - } -} - -process_struct! { - syn::TypeTraitObject { - dyn_token, - bounds, - } -} - -process_struct! { - syn::TypeTuple { - paren_token, - elems, - } -} - -process_struct! { - syn::UseGlob { - star_token, - } -} - -process_struct! { - syn::UseGroup { - brace_token, - items, - } -} - -process_struct! { - syn::UseName { - ident, - } -} - -process_struct! { - syn::UsePath { - ident, - colon2_token, - tree, - } -} - -process_struct! { - syn::UseRename { - ident, - as_token, - rename, - } -} - -process_struct! { - syn::Variadic { - attrs, - pat, - dots, - comma, - } -} - -process_struct! { - syn::Variant { - attrs, - ident, - fields, - discriminant, - } -} - -process_struct! { - syn::VisRestricted { - pub_token, - paren_token, - in_token, - path, - } -} - -process_struct! { - syn::WhereClause { - where_token, - predicates, - } -} - -macro_rules! process_enum { - ($path:path { - $($variant:ident$(($($field:ident),* $(,)?))?,)* - }) => { - impl Process

for $path { - fn process(self, state: &mut State

) -> Option> { - match self { - $(Self::$variant$(($($field),*))? => Some(($($($field.process(state)?,)*)?).call(|($($($field,)*)?)| Self::$variant$(($($field),*))?)),)* - } - } - } - }; - ($path:path { - $($variant:ident$(($($field:ident),* $(,)?))?,)* - #[no_op] - _, - }) => { - impl Process

for $path { - fn process(self, state: &mut State

) -> Option> { - #![allow(unused_variables)] - match self { - $(Self::$variant$(($($field),*))? => Some(($($($field.process(state)?,)*)?).call(|($($($field,)*)?)| Self::$variant$(($($field),*))?)),)* - _ => Some(Output::new(self)), - } - } - } - }; -} - -process_enum! { - syn::AttrStyle { - Outer, - Inner(f0), - } -} - -process_enum! { - syn::BinOp { - Add(f0), - Sub(f0), - Mul(f0), - Div(f0), - Rem(f0), - And(f0), - Or(f0), - BitXor(f0), - BitAnd(f0), - BitOr(f0), - Shl(f0), - Shr(f0), - Eq(f0), - Lt(f0), - Le(f0), - Ne(f0), - Ge(f0), - Gt(f0), - AddAssign(f0), - SubAssign(f0), - MulAssign(f0), - DivAssign(f0), - RemAssign(f0), - BitXorAssign(f0), - BitAndAssign(f0), - BitOrAssign(f0), - ShlAssign(f0), - ShrAssign(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::CapturedParam { - Lifetime(f0), - Ident(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::Data { - Struct(f0), - Enum(f0), - Union(f0), - } -} - -process_enum! { - syn::Expr { - Array(f0), - Assign(f0), - Async(f0), - Await(f0), - Binary(f0), - Block(f0), - Break(f0), - Call(f0), - Cast(f0), - Closure(f0), - Const(f0), - Continue(f0), - Field(f0), - ForLoop(f0), - Group(f0), - If(f0), - Index(f0), - Infer(f0), - Let(f0), - Lit(f0), - Loop(f0), - Macro(f0), - Match(f0), - MethodCall(f0), - Paren(f0), - Path(f0), - Range(f0), - RawAddr(f0), - Reference(f0), - Repeat(f0), - Return(f0), - Struct(f0), - Try(f0), - TryBlock(f0), - Tuple(f0), - Unary(f0), - Unsafe(f0), - Verbatim(f0), - While(f0), - Yield(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::FieldMutability { - None, - #[no_op] - _, - } -} - -process_enum! { - syn::Fields { - Named(f0), - Unnamed(f0), - Unit, - } -} - -process_enum! { - syn::FnArg { - Receiver(f0), - Typed(f0), - } -} - -process_enum! { - syn::ForeignItem { - Fn(f0), - Static(f0), - Type(f0), - Macro(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::GenericArgument { - Lifetime(f0), - Type(f0), - Const(f0), - AssocType(f0), - AssocConst(f0), - Constraint(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::GenericParam { - Lifetime(f0), - Type(f0), - Const(f0), - } -} - -process_enum! { - syn::ImplItem { - Const(f0), - Fn(f0), - Type(f0), - Macro(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::ImplRestriction { - #[no_op] - _, - } -} - -process_enum! { - syn::Lit { - Str(f0), - ByteStr(f0), - CStr(f0), - Byte(f0), - Char(f0), - Int(f0), - Float(f0), - Bool(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::MacroDelimiter { - Paren(f0), - Brace(f0), - Bracket(f0), - } -} - -process_enum! { - syn::Member { - Named(f0), - Unnamed(f0), - } -} - -process_enum! { - syn::Meta { - Path(f0), - List(f0), - NameValue(f0), - } -} - -process_enum! { - syn::Pat { - Const(f0), - Ident(f0), - Lit(f0), - Macro(f0), - Or(f0), - Paren(f0), - Path(f0), - Range(f0), - Reference(f0), - Rest(f0), - Slice(f0), - Struct(f0), - Tuple(f0), - TupleStruct(f0), - Type(f0), - Verbatim(f0), - Wild(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::PathArguments { - None, - AngleBracketed(f0), - Parenthesized(f0), - } -} - -process_enum! { - syn::PointerMutability { - Const(f0), - Mut(f0), - } -} - -process_enum! { - syn::RangeLimits { - HalfOpen(f0), - Closed(f0), - } -} - -process_enum! { - syn::ReturnType { - Default, - Type(f0, f1), - } -} - -process_enum! { - syn::StaticMutability { - Mut(f0), - None, - #[no_op] - _, - } -} - -process_enum! { - syn::Stmt { - Local(f0), - Item(f0), - Expr(f0, f1), - Macro(f0), - } -} - -process_enum! { - syn::TraitBoundModifier { - None, - Maybe(f0), - } -} - -process_enum! { - syn::TraitItem { - Const(f0), - Fn(f0), - Type(f0), - Macro(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::Type { - Array(f0), - BareFn(f0), - Group(f0), - ImplTrait(f0), - Infer(f0), - Macro(f0), - Never(f0), - Paren(f0), - Path(f0), - Ptr(f0), - Reference(f0), - Slice(f0), - TraitObject(f0), - Tuple(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::TypeParamBound { - Trait(f0), - Lifetime(f0), - PreciseCapture(f0), - Verbatim(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::UnOp { - Deref(f0), - Not(f0), - Neg(f0), - #[no_op] - _, - } -} - -process_enum! { - syn::UseTree { - Path(f0), - Name(f0), - Rename(f0), - Glob(f0), - Group(f0), - } -} - -process_enum! { - syn::Visibility { - Public(f0), - Restricted(f0), - Inherited, - } -} - -process_enum! { - syn::WherePredicate { - Lifetime(f0), - Type(f0), - #[no_op] - _, - } -} - -struct TopItem(syn::Item); - -impl Process

for TopItem { - fn process(self, state: &mut State

) -> Option> { - match self.0 { - syn::Item::Const(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Enum(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::ExternCrate(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Fn(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::ForeignMod(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Impl(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Macro(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Mod(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Static(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Struct(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Trait(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::TraitAlias(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Type(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Union(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - syn::Item::Use(item) => Some(item.process(state)?.map(Into::into).map(TopItem)), - _ => Some(Output::new(self)), - } - } -} - -pub(crate) fn process_cfgs(item: syn::Item, cfgs: Cfgs) -> syn::Result> { - let mut state = State:: { - cfgs, - errors: Errors::new(), - _phantom: PhantomData, - }; - let retval = TopItem(item).process(&mut state).map(|v| v.0 .0); - state.errors.finish()?; - Ok(retval) -} - -pub(crate) fn collect_cfgs(item: syn::Item) -> syn::Result> { - let mut state = State:: { - cfgs: Cfgs::default(), - errors: Errors::new(), - _phantom: PhantomData, - }; - let (None | Some(Output(()))) = TopItem(item).process(&mut state); - state.errors.finish()?; - Ok(state.cfgs) -} diff --git a/crates/fayalite/build.rs b/crates/fayalite/build.rs index c6680d5..24d8f31 100644 --- a/crates/fayalite/build.rs +++ b/crates/fayalite/build.rs @@ -5,9 +5,6 @@ use std::{env, fs, path::Path}; fn main() { println!("cargo::rustc-check-cfg=cfg(todo)"); - println!("cargo::rustc-check-cfg=cfg(cfg_false_for_tests)"); - println!("cargo::rustc-check-cfg=cfg(cfg_true_for_tests)"); - println!("cargo::rustc-cfg=cfg_true_for_tests"); let path = "visit_types.json"; println!("cargo::rerun-if-changed={path}"); println!("cargo::rerun-if-changed=build.rs"); diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 88fe169..fba7ada 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -11,59 +11,6 @@ extern crate self as fayalite; #[doc(hidden)] pub use std as __std; -#[doc(hidden)] -#[macro_export] -macro_rules! __cfg_expansion_helper { - ( - [ - $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* - ] - [ - $cfg:ident($($expr:tt)*), - $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* - ] - // pass as tt so we get right span for attribute - $after_evaluation_attr:tt $after_evaluation_body:tt - ) => { - #[$cfg($($expr)*)] - $crate::__cfg_expansion_helper! { - [ - $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* - $cfg($($expr)*) = true, - ] - [ - $($unevaluated_cfgs($($unevaluated_exprs)*),)* - ] - $after_evaluation_attr $after_evaluation_body - } - #[$cfg(not($($expr)*))] - $crate::__cfg_expansion_helper! { - [ - $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* - $cfg($($expr)*) = false, - ] - [ - $($unevaluated_cfgs($($unevaluated_exprs)*),)* - ] - $after_evaluation_attr $after_evaluation_body - } - }; - ( - [ - $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* - ] - [] - // don't use #[...] so we get right span for `#` and `[]` of attribute - {$($after_evaluation_attr:tt)*} {$($after_evaluation_body:tt)*} - ) => { - $($after_evaluation_attr)* - #[__evaluated_cfgs([ - $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* - ])] - $($after_evaluation_body)* - }; -} - #[doc(inline)] /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates /// a [`Module`][`::fayalite::module::Module`] when called. diff --git a/crates/fayalite/src/sim.rs b/crates/fayalite/src/sim.rs index cb6228d..10fbb92 100644 --- a/crates/fayalite/src/sim.rs +++ b/crates/fayalite/src/sim.rs @@ -1601,14 +1601,6 @@ impl MakeTraceDeclTarget { } } -struct DebugOpaque(T); - -impl fmt::Debug for DebugOpaque { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("<...>") - } -} - #[derive(Debug)] pub struct Compiler { insns: Insns, @@ -1630,7 +1622,6 @@ pub struct Compiler { registers: Vec, traces: SimTraces>>, memories: Vec, - dump_assignments_dot: Option>>, } impl Compiler { @@ -1656,14 +1647,8 @@ impl Compiler { registers: Vec::new(), traces: SimTraces(Vec::new()), memories: Vec::new(), - dump_assignments_dot: None, } } - #[doc(hidden)] - /// This is explicitly unstable and may be changed/removed at any time - pub fn dump_assignments_dot(&mut self, callback: Box) { - self.dump_assignments_dot = Some(DebugOpaque(callback)); - } fn new_sim_trace(&mut self, kind: SimTraceKind) -> TraceScalarId { let id = TraceScalarId(self.traces.0.len()); self.traces.0.push(SimTrace { @@ -4665,11 +4650,12 @@ impl Compiler { fn process_assignments(&mut self) { self.assignments .finalize(self.insns.state_layout().ty.clone().into()); - if let Some(DebugOpaque(dump_assignments_dot)) = &self.dump_assignments_dot { - let graph = - petgraph::graph::DiGraph::<_, _, usize>::from_elements(self.assignments.elements()); - dump_assignments_dot(&petgraph::dot::Dot::new(&graph)); - } + println!( + "{:#?}", + petgraph::dot::Dot::new(&petgraph::graph::DiGraph::<_, _, usize>::from_elements( + self.assignments.elements() + )) + ); let assignments_order: Vec<_> = match petgraph::algo::toposort(&self.assignments, None) { Ok(nodes) => nodes .into_iter() @@ -6443,7 +6429,7 @@ impl_to_sim_value_for_int_value!(SIntValue, SInt, SIntType); struct SimulationImpl { state: interpreter::State, io: Expr, - uninitialized_inputs: HashMap>, + uninitialized_inputs: HashSet, io_targets: HashMap>, made_initial_step: bool, needs_settle: bool, @@ -6497,52 +6483,37 @@ impl SimulationImpl { .field("clocks_triggered", clocks_triggered) .finish_non_exhaustive() } - /// returns `true` if `target` or any sub-targets are uninitialized inputs - fn parse_io(&mut self, target: Target, value: CompiledValue) -> bool { + fn parse_io(&mut self, target: Target, value: CompiledValue) { self.io_targets.insert(target, value); match value.layout.body { CompiledTypeLayoutBody::Scalar => match target.flow() { - Flow::Source => false, + Flow::Source => {} Flow::Sink => { - self.uninitialized_inputs.insert(target, vec![]); - true + self.uninitialized_inputs.insert(target); } Flow::Duplex => unreachable!(), }, CompiledTypeLayoutBody::Array { .. } => { let value = value.map_ty(Array::from_canonical); - let mut sub_targets = Vec::new(); for index in 0..value.layout.ty.len() { - let sub_target = target.join( - TargetPathElement::from(TargetPathArrayElement { index }).intern_sized(), + self.parse_io( + target.join( + TargetPathElement::from(TargetPathArrayElement { index }) + .intern_sized(), + ), + value.element(index), ); - if self.parse_io(sub_target, value.element(index)) { - sub_targets.push(sub_target); - } - } - if sub_targets.is_empty() { - false - } else { - self.uninitialized_inputs.insert(target, sub_targets); - true } } CompiledTypeLayoutBody::Bundle { .. } => { let value = value.map_ty(Bundle::from_canonical); - let mut sub_targets = Vec::new(); for BundleField { name, .. } in value.layout.ty.fields() { - let sub_target = target.join( - TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), + self.parse_io( + target.join( + TargetPathElement::from(TargetPathBundleField { name }).intern_sized(), + ), + value.field_by_name(name), ); - if self.parse_io(sub_target, value.field_by_name(name)) { - sub_targets.push(sub_target); - } - } - if sub_targets.is_empty() { - false - } else { - self.uninitialized_inputs.insert(target, sub_targets); - true } } } @@ -6551,7 +6522,7 @@ impl SimulationImpl { let mut retval = Self { state: State::new(compiled.insns), io: compiled.io.to_expr(), - uninitialized_inputs: HashMap::new(), + uninitialized_inputs: HashSet::new(), io_targets: HashMap::new(), made_initial_step: false, needs_settle: true, @@ -6820,35 +6791,6 @@ impl SimulationImpl { }; panic!("simulator read/write expression must not have dynamic array indexes"); } - fn mark_target_as_initialized(&mut self, mut target: Target) { - fn remove_target_and_children( - uninitialized_inputs: &mut HashMap>, - target: Target, - ) { - let Some(children) = uninitialized_inputs.remove(&target) else { - return; - }; - for child in children { - remove_target_and_children(uninitialized_inputs, child); - } - } - remove_target_and_children(&mut self.uninitialized_inputs, target); - while let Some(target_child) = target.child() { - let parent = target_child.parent(); - for child in self - .uninitialized_inputs - .get(&*parent) - .map(|v| &**v) - .unwrap_or(&[]) - { - if self.uninitialized_inputs.contains_key(child) { - return; - } - } - target = *parent; - self.uninitialized_inputs.remove(&target); - } - } #[track_caller] fn read_helper(&mut self, io: Expr) -> CompiledValue { let Some(target) = io.target() else { @@ -6868,7 +6810,7 @@ impl SimulationImpl { self.settle(); } Flow::Sink => { - if self.uninitialized_inputs.contains_key(&*target) { + if self.uninitialized_inputs.contains(&*target) { panic!("can't read from an uninitialized input"); } } @@ -6891,7 +6833,7 @@ impl SimulationImpl { Flow::Duplex => unreachable!(), } if !self.made_initial_step { - self.mark_target_as_initialized(*target); + self.uninitialized_inputs.remove(&*target); } self.needs_settle = true; compiled_value @@ -7194,11 +7136,11 @@ pub struct Simulation { io: Expr, } -struct SortedSetDebug<'a, T, V>(&'a HashMap); +struct SortedSetDebug<'a, T>(&'a HashSet); -impl fmt::Debug for SortedSetDebug<'_, T, V> { +impl fmt::Debug for SortedSetDebug<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut entries = Vec::from_iter(self.0.iter().map(|(v, _)| { + let mut entries = Vec::from_iter(self.0.iter().map(|v| { if f.alternate() { format!("{v:#?}") } else { diff --git a/crates/fayalite/src/sim/vcd.rs b/crates/fayalite/src/sim/vcd.rs index fde30be..b8248e3 100644 --- a/crates/fayalite/src/sim/vcd.rs +++ b/crates/fayalite/src/sim/vcd.rs @@ -5,7 +5,6 @@ use crate::{ enum_::{Enum, EnumType}, expr::Flow, int::UInt, - intern::{Intern, Interned}, sim::{ time::{SimDuration, SimInstant}, TraceArray, TraceAsyncReset, TraceBool, TraceBundle, TraceClock, TraceDecl, @@ -16,73 +15,12 @@ use crate::{ }, }; use bitvec::{order::Lsb0, slice::BitSlice}; -use hashbrown::{hash_map::Entry, HashMap}; use std::{ - fmt::{self, Write as _}, - io, mem, + fmt, + io::{self, Write}, + mem, }; -#[derive(Default)] -struct Scope { - last_inserted: HashMap, usize>, -} - -#[derive(Copy, Clone)] -struct VerilogIdentifier { - unescaped_name: Interned, -} - -impl VerilogIdentifier { - fn needs_escape(self) -> bool { - // we only allow ascii, so we can just check bytes - let Some((&first, rest)) = self.unescaped_name.as_bytes().split_first() else { - unreachable!("Scope::new_identifier guarantees a non-empty name"); - }; - if !first.is_ascii_alphabetic() && first != b'_' { - true - } else { - rest.iter() - .any(|&ch| !ch.is_ascii_alphanumeric() && ch != b'_' && ch != b'$') - } - } -} - -impl fmt::Display for VerilogIdentifier { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.needs_escape() { - f.write_str("\\")?; - } - write!(f, "{}", Escaped(self.unescaped_name)) - } -} - -impl Scope { - fn new_identifier(&mut self, unescaped_name: Interned) -> VerilogIdentifier { - let next_disambiguator = match self.last_inserted.entry(unescaped_name) { - Entry::Vacant(entry) => { - entry.insert(1); - return VerilogIdentifier { unescaped_name }; - } - Entry::Occupied(entry) => entry.get() + 1, - }; - let mut disambiguated_name = String::from(&*unescaped_name); - for disambiguator in next_disambiguator.. { - disambiguated_name.truncate(unescaped_name.len()); - write!(disambiguated_name, "_{disambiguator}").expect("can't fail"); - if let Entry::Vacant(entry) = self.last_inserted.entry((*disambiguated_name).intern()) { - let retval = VerilogIdentifier { - unescaped_name: *entry.key(), - }; - entry.insert(1); - // speed up future searches - self.last_inserted.insert(unescaped_name, disambiguator); - return retval; - } - } - panic!("too many names"); - } -} - pub struct VcdWriterDecls { writer: W, timescale: SimDuration, @@ -159,20 +97,14 @@ impl fmt::Debug for VcdWriterDecls { } } -/// pass in scope to ensure it's not available in child scope fn write_vcd_scope( writer: &mut W, scope_type: &str, - scope_name: Interned, - scope: &mut Scope, - f: impl FnOnce(&mut W, &mut Scope) -> io::Result, + scope_name: &str, + f: impl FnOnce(&mut W) -> io::Result, ) -> io::Result { - writeln!( - writer, - "$scope {scope_type} {} $end", - scope.new_identifier(scope_name), - )?; - let retval = f(writer, &mut Scope::default())?; + writeln!(writer, "$scope {scope_type} {scope_name} $end")?; + let retval = f(writer)?; writeln!(writer, "$upscope $end")?; Ok(retval) } @@ -211,28 +143,24 @@ trait_arg! { struct ArgModule<'a> { properties: &'a mut VcdWriterProperties, - scope: &'a mut Scope, } impl<'a> ArgModule<'a> { fn reborrow(&mut self) -> ArgModule<'_> { ArgModule { properties: self.properties, - scope: self.scope, } } } struct ArgModuleBody<'a> { properties: &'a mut VcdWriterProperties, - scope: &'a mut Scope, } impl<'a> ArgModuleBody<'a> { fn reborrow(&mut self) -> ArgModuleBody<'_> { ArgModuleBody { properties: self.properties, - scope: self.scope, } } } @@ -242,7 +170,6 @@ struct ArgInType<'a> { sink_var_type: &'static str, duplex_var_type: &'static str, properties: &'a mut VcdWriterProperties, - scope: &'a mut Scope, } impl<'a> ArgInType<'a> { @@ -252,7 +179,6 @@ impl<'a> ArgInType<'a> { sink_var_type: self.sink_var_type, duplex_var_type: self.duplex_var_type, properties: self.properties, - scope: self.scope, } } } @@ -300,42 +226,55 @@ fn write_vcd_id(writer: &mut W, mut id: usize) -> io::Result<()> { Ok(()) } -struct Escaped(T); - -impl fmt::Display for Escaped { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // escaping rules from function GTKWave uses to decode VCD strings: - // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 - struct Wrapper(W); - impl fmt::Write for Wrapper { - fn write_str(&mut self, s: &str) -> fmt::Result { - for byte in s.bytes() { - match byte { - b'\\' | b'\'' | b'"' | b'?' => { - self.0.write_str("\\")?; - self.0.write_char(byte as char)?; - } - b'\n' => self.0.write_str(r"\n")?, - b'\r' => self.0.write_str(r"\r")?, - b'\t' => self.0.write_str(r"\t")?, - 0x7 => self.0.write_str(r"\a")?, - 0x8 => self.0.write_str(r"\b")?, - 0xC => self.0.write_str(r"\f")?, - 0xB => self.0.write_str(r"\v")?, - _ => { - if byte.is_ascii_graphic() { - self.0.write_char(byte as char)?; - } else { - write!(self.0, r"\x{byte:02x}")?; - } +fn write_escaped(writer: &mut W, value: impl fmt::Display) -> io::Result<()> { + // escaping rules from function GTKWave uses to decode VCD strings: + // https://github.com/gtkwave/gtkwave/blob/491f24d7e8619cfc1fcc65704ee5c967d1083c18/lib/libfst/fstapi.c#L7090 + struct Wrapper(W); + impl io::Write for Wrapper { + fn write(&mut self, buf: &[u8]) -> io::Result { + if buf.is_empty() { + return self.0.write(buf); + } + let mut retval = 0; + for &byte in buf { + match byte { + b'\\' | b'\'' | b'"' | b'?' => self.0.write_all(&[b'\\', byte])?, + b'\n' => self.0.write_all(br"\n")?, + b'\r' => self.0.write_all(br"\r")?, + b'\t' => self.0.write_all(br"\t")?, + 0x7 => self.0.write_all(br"\a")?, + 0x8 => self.0.write_all(br"\b")?, + 0xC => self.0.write_all(br"\f")?, + 0xB => self.0.write_all(br"\v")?, + _ => { + if byte.is_ascii_graphic() { + self.0.write_all(&[byte])?; + } else { + write!(self.0, r"\x{byte:02x}")?; } } } - Ok(()) + retval += 1; } + Ok(retval) + } + + fn flush(&mut self) -> io::Result<()> { + self.0.flush() } - write!(Wrapper(f), "{}", self.0) } + write!(Wrapper(writer), "{value}") +} + +fn is_unescaped_verilog_identifier(ident: &str) -> bool { + // we only allow ascii, so we can just check bytes + let Some((&first, rest)) = ident.as_bytes().split_first() else { + return false; // empty string is not an identifier + }; + (first.is_ascii_alphabetic() || first == b'_') + && rest + .iter() + .all(|&ch| ch.is_ascii_alphanumeric() || ch == b'_' || ch == b'$') } fn write_vcd_var( @@ -345,7 +284,7 @@ fn write_vcd_var( var_type: &str, size: usize, location: TraceLocation, - name: VerilogIdentifier, + name: &str, ) -> io::Result<()> { let id = match location { TraceLocation::Scalar(id) => id.as_usize(), @@ -380,7 +319,12 @@ fn write_vcd_var( }; write!(writer, "$var {var_type} {size} ")?; write_vcd_id(writer, id)?; - writeln!(writer, " {name} $end") + writer.write_all(b" ")?; + if !is_unescaped_verilog_identifier(name) { + writer.write_all(b"\\")?; + } + write_escaped(writer, name)?; + writer.write_all(b" $end\n") } impl WriteTrace for TraceUInt { @@ -390,7 +334,6 @@ impl WriteTrace for TraceUInt { sink_var_type, duplex_var_type, properties, - scope, } = arg.in_type(); let Self { location, @@ -413,7 +356,7 @@ impl WriteTrace for TraceUInt { var_type, ty.width(), location, - scope.new_identifier(name), + &name, ) } } @@ -478,7 +421,6 @@ impl WriteTrace for TraceEnumDiscriminant { sink_var_type: _, duplex_var_type: _, properties, - scope, } = arg.in_type(); let Self { location, @@ -493,7 +435,7 @@ impl WriteTrace for TraceEnumDiscriminant { "string", 1, location, - scope.new_identifier(name), + &name, ) } } @@ -565,11 +507,11 @@ impl WriteTrace for TraceScope { impl WriteTrace for TraceModule { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModule { properties, scope } = arg.module(); + let ArgModule { properties } = arg.module(); let Self { name, children } = self; - write_vcd_scope(writer, "module", name, scope, |writer, scope| { + write_vcd_scope(writer, "module", &name, |writer| { for child in children { - child.write_trace(writer, ArgModuleBody { properties, scope })?; + child.write_trace(writer, ArgModuleBody { properties })?; } Ok(()) }) @@ -578,7 +520,7 @@ impl WriteTrace for TraceModule { impl WriteTrace for TraceInstance { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, instance_io, @@ -592,16 +534,15 @@ impl WriteTrace for TraceInstance { sink_var_type: "wire", duplex_var_type: "wire", properties, - scope, }, )?; - module.write_trace(writer, ArgModule { properties, scope }) + module.write_trace(writer, ArgModule { properties }) } } impl WriteTrace for TraceMem { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { id, name, @@ -610,41 +551,27 @@ impl WriteTrace for TraceMem { ports, array_type, } = self; - write_vcd_scope(writer, "struct", name, scope, |writer, scope| { - write_vcd_scope( - writer, - "struct", - "contents".intern(), - scope, - |writer, scope| { - for element_index in 0..array_type.len() { - write_vcd_scope( + write_vcd_scope(writer, "struct", &*name, |writer| { + write_vcd_scope(writer, "struct", "contents", |writer| { + for element_index in 0..array_type.len() { + write_vcd_scope(writer, "struct", &format!("[{element_index}]"), |writer| { + properties.memory_properties[id.as_usize()].element_index = element_index; + properties.memory_properties[id.as_usize()].element_part_index = 0; + element_type.write_trace( writer, - "struct", - Intern::intern_owned(format!("[{element_index}]")), - scope, - |writer, scope| { - properties.memory_properties[id.as_usize()].element_index = - element_index; - properties.memory_properties[id.as_usize()].element_part_index = 0; - element_type.write_trace( - writer, - ArgInType { - source_var_type: "reg", - sink_var_type: "reg", - duplex_var_type: "reg", - properties, - scope, - }, - ) + ArgInType { + source_var_type: "reg", + sink_var_type: "reg", + duplex_var_type: "reg", + properties, }, - )?; - } - Ok(()) - }, - )?; + ) + })?; + } + Ok(()) + })?; for port in ports { - port.write_trace(writer, ArgModuleBody { properties, scope })?; + port.write_trace(writer, ArgModuleBody { properties })?; } Ok(()) }) @@ -653,7 +580,7 @@ impl WriteTrace for TraceMem { impl WriteTrace for TraceMemPort { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, bundle, @@ -666,7 +593,6 @@ impl WriteTrace for TraceMemPort { sink_var_type: "wire", duplex_var_type: "wire", properties, - scope, }, ) } @@ -674,7 +600,7 @@ impl WriteTrace for TraceMemPort { impl WriteTrace for TraceWire { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -687,7 +613,6 @@ impl WriteTrace for TraceWire { sink_var_type: "wire", duplex_var_type: "wire", properties, - scope, }, ) } @@ -695,7 +620,7 @@ impl WriteTrace for TraceWire { impl WriteTrace for TraceReg { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -708,7 +633,6 @@ impl WriteTrace for TraceReg { sink_var_type: "reg", duplex_var_type: "reg", properties, - scope, }, ) } @@ -716,7 +640,7 @@ impl WriteTrace for TraceReg { impl WriteTrace for TraceModuleIO { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgModuleBody { properties, scope } = arg.module_body(); + let ArgModuleBody { properties } = arg.module_body(); let Self { name: _, child, @@ -730,7 +654,6 @@ impl WriteTrace for TraceModuleIO { sink_var_type: "wire", duplex_var_type: "wire", properties, - scope, }, ) } @@ -738,31 +661,16 @@ impl WriteTrace for TraceModuleIO { impl WriteTrace for TraceBundle { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - } = arg.in_type(); + let mut arg = arg.in_type(); let Self { name, fields, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + write_vcd_scope(writer, "struct", &name, |writer| { for field in fields { - field.write_trace( - writer, - ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - }, - )?; + field.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -771,31 +679,16 @@ impl WriteTrace for TraceBundle { impl WriteTrace for TraceArray { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - } = arg.in_type(); + let mut arg = arg.in_type(); let Self { name, elements, ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", name, scope, |writer, scope| { + write_vcd_scope(writer, "struct", &name, |writer| { for element in elements { - element.write_trace( - writer, - ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - }, - )?; + element.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -804,13 +697,7 @@ impl WriteTrace for TraceArray { impl WriteTrace for TraceEnumWithFields { fn write_trace(self, writer: &mut W, mut arg: A) -> io::Result<()> { - let ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - } = arg.in_type(); + let mut arg = arg.in_type(); let Self { name, discriminant, @@ -818,28 +705,10 @@ impl WriteTrace for TraceEnumWithFields { ty: _, flow: _, } = self; - write_vcd_scope(writer, "struct", name, scope, |writer, scope| { - discriminant.write_trace( - writer, - ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - }, - )?; + write_vcd_scope(writer, "struct", &name, |writer| { + discriminant.write_trace(writer, arg.reborrow())?; for field in non_empty_fields { - field.write_trace( - writer, - ArgInType { - source_var_type, - sink_var_type, - duplex_var_type, - properties, - scope, - }, - )?; + field.write_trace(writer, arg.reborrow())?; } Ok(()) }) @@ -875,7 +744,6 @@ impl TraceWriterDecls for VcdWriterDecls { &mut writer, ArgModule { properties: &mut properties, - scope: &mut Scope::default(), }, )?; writeln!(writer, "$enddefinitions $end")?; @@ -930,7 +798,9 @@ fn write_string_value_change( value: impl fmt::Display, id: usize, ) -> io::Result<()> { - write!(writer, "s{} ", Escaped(value))?; + writer.write_all(b"s")?; + write_escaped(writer, value)?; + writer.write_all(b" ")?; write_vcd_id(writer, id)?; writer.write_all(b"\n") } @@ -1076,49 +946,3 @@ impl fmt::Debug for VcdWriter { .finish_non_exhaustive() } } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_scope() { - let mut scope = Scope::default(); - assert_eq!(&*scope.new_identifier("foo".intern()).unescaped_name, "foo"); - assert_eq!( - &*scope.new_identifier("foo_0".intern()).unescaped_name, - "foo_0" - ); - assert_eq!( - &*scope.new_identifier("foo_1".intern()).unescaped_name, - "foo_1" - ); - assert_eq!( - &*scope.new_identifier("foo_3".intern()).unescaped_name, - "foo_3" - ); - assert_eq!( - &*scope.new_identifier("foo".intern()).unescaped_name, - "foo_2" - ); - assert_eq!( - &*scope.new_identifier("foo".intern()).unescaped_name, - "foo_4" - ); - assert_eq!( - &*scope.new_identifier("foo_0".intern()).unescaped_name, - "foo_0_2" - ); - assert_eq!( - &*scope.new_identifier("foo_1".intern()).unescaped_name, - "foo_1_2" - ); - for i in 5..1000u64 { - // verify it actually picks the next available identifier with no skips or duplicates - assert_eq!( - *scope.new_identifier("foo".intern()).unescaped_name, - format!("foo_{i}"), - ); - } - } -} diff --git a/crates/fayalite/src/util/ready_valid.rs b/crates/fayalite/src/util/ready_valid.rs index ac08a64..82d3f1e 100644 --- a/crates/fayalite/src/util/ready_valid.rs +++ b/crates/fayalite/src/util/ready_valid.rs @@ -49,18 +49,6 @@ impl ReadyValid { } } -/// This debug port is only meant to assist the formal proof of the queue. -#[cfg(test)] -#[doc(hidden)] -#[hdl] -pub struct QueueDebugPort { - #[hdl(flip)] - index_to_check: Index, - stored: Element, - inp_index: Index, - out_index: Index, -} - #[hdl_module] pub fn queue( ty: T, @@ -190,22 +178,6 @@ pub fn queue( } } } - // These debug ports expose some internal state during the Induction phase - // of Formal Verification. They are not present in normal use. - #[cfg(test)] - { - #[hdl] - let dbg: QueueDebugPort = m.output(QueueDebugPort[ty][index_ty]); - // read the memory word currently stored at some fixed index - let debug_port = mem.new_read_port(); - connect(debug_port.addr, dbg.index_to_check); - connect(debug_port.en, true); - connect(debug_port.clk, cd.clk); - connect(dbg.stored, debug_port.data); - // also expose the current read and write indices - connect(dbg.inp_index, inp_index_reg); - connect(dbg.out_index, out_index_reg); - } } #[cfg(test)] @@ -224,23 +196,13 @@ mod tests { format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"), queue_test(capacity, inp_ready_is_comb, out_valid_is_comb), FormalMode::Prove, - 2, + 14, None, ExportOptions { simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts), ..ExportOptions::default() }, ); - /// Formal verification of the FIFO queue - /// - /// The strategy derives from the observation that, if we filter its - /// input and output streams to consider just one in every N reads and - /// writes (where N is the FIFO capacity), then the FIFO effectively - /// behaves as a one-entry FIFO. - /// - /// In particular, any counterexample of the full FIFO behaving badly - /// will also be caught by one of the filtered versions (one which - /// happens to be in phase with the offending input or output). #[hdl_module] fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) { #[hdl] @@ -255,8 +217,6 @@ mod tests { rst: formal_reset().to_reset(), }, ); - - // random input data #[hdl] let inp_data: HdlOption> = wire(); #[hdl] @@ -265,26 +225,16 @@ mod tests { } else { connect(inp_data, HdlNone()); } - - // assert output ready at random #[hdl] let out_ready: Bool = wire(); connect(out_ready, any_seq(Bool)); - - // The current number of elements in the FIFO ranges from zero to - // maximum capacity, inclusive. - let count_ty = UInt::range_inclusive(0..=capacity.get()); - // type for counters that wrap around at the FIFO capacity - let index_ty = UInt::range(0..capacity.get()); - - // among all entries of the FIFO internal circular memory, choose - // one at random to check + let index_ty: UInt<32> = UInt::TYPE; #[hdl] - let index_to_check = wire(index_ty); + let index_to_check = wire(); connect(index_to_check, any_const(index_ty)); - hdl_assume(clk, index_to_check.cmp_lt(capacity.get()), ""); - - // instantiate and connect the queue + let index_max = !index_ty.zero(); + // we saturate at index_max, so only check indexes where we properly maintain position + hdl_assume(clk, index_to_check.cmp_ne(index_max), ""); #[hdl] let dut = instance(queue( UInt[ConstUsize::<8>], @@ -295,172 +245,109 @@ mod tests { connect(dut.cd, cd); connect(dut.inp.data, inp_data); connect(dut.out.ready, out_ready); + hdl_assume( + clk, + index_to_check.cmp_ne(!Expr::ty(index_to_check).zero()), + "", + ); - // Keep an independent count of words in the FIFO. Ensure that - // it's always correct, and never overflows. #[hdl] - let expected_count_reg = reg_builder().clock_domain(cd).reset(count_ty.zero()); + let expected_count_reg = reg_builder().clock_domain(cd).reset(0u32); + #[hdl] + let next_expected_count = wire(); + connect(next_expected_count, expected_count_reg); + connect(expected_count_reg, next_expected_count); #[hdl] if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) { - hdl_assert(clk, expected_count_reg.cmp_ne(capacity.get()), ""); - connect_any(expected_count_reg, expected_count_reg + 1u8); + connect_any(next_expected_count, expected_count_reg + 1u8); } else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) { - hdl_assert(clk, expected_count_reg.cmp_ne(count_ty.zero()), ""); - connect_any(expected_count_reg, expected_count_reg - 1u8); + connect_any(next_expected_count, expected_count_reg - 1u8); } - hdl_assert(clk, expected_count_reg.cmp_eq(dut.count), ""); + hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), ""); + + #[hdl] + let prev_out_ready_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + connect_any( + prev_out_ready_reg, + (prev_out_ready_reg << 1) | out_ready.cast_to(UInt[1]), + ); + #[hdl] + let prev_inp_valid_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3); + connect_any( + prev_inp_valid_reg, + (prev_inp_valid_reg << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]), + ); + hdl_assume( + clk, + (prev_out_ready_reg & prev_inp_valid_reg).cmp_ne(0u8), + "", + ); - // keep an independent write index into the FIFO's circular buffer #[hdl] let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - if ReadyValid::firing(dut.inp) { + let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8); + + #[hdl] + if let HdlSome(data) = ReadyValid::firing_data(dut.inp) { #[hdl] - if inp_index_reg.cmp_ne(capacity.get() - 1) { + if inp_index_reg.cmp_lt(index_max) { connect_any(inp_index_reg, inp_index_reg + 1u8); - } else { - connect_any(inp_index_reg, 0_hdl_u0); + #[hdl] + if inp_index_reg.cmp_eq(index_to_check) { + connect(stored_inp_data_reg, data); + } } } - // keep an independent read index into the FIFO's circular buffer + #[hdl] + if inp_index_reg.cmp_lt(index_to_check) { + hdl_assert(clk, stored_inp_data_reg.cmp_eq(0u8), ""); + } + #[hdl] let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero()); #[hdl] - if ReadyValid::firing(dut.out) { + let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8); + + #[hdl] + if let HdlSome(data) = ReadyValid::firing_data(dut.out) { #[hdl] - if out_index_reg.cmp_ne(capacity.get() - 1) { + if out_index_reg.cmp_lt(index_max) { connect_any(out_index_reg, out_index_reg + 1u8); - } else { - connect_any(out_index_reg, 0_hdl_u0); - } - } - - // filter the input data stream, predicated by the read index - // matching the chosen position in the FIFO's circular buffer - #[hdl] - let inp_index_matches = wire(); - connect(inp_index_matches, inp_index_reg.cmp_eq(index_to_check)); - #[hdl] - let inp_firing_data = wire(); - connect(inp_firing_data, HdlNone()); - #[hdl] - if inp_index_matches { - connect(inp_firing_data, ReadyValid::firing_data(dut.inp)); - } - - // filter the output data stream, predicated by the write index - // matching the chosen position in the FIFO's circular buffer - #[hdl] - let out_index_matches = wire(); - connect(out_index_matches, out_index_reg.cmp_eq(index_to_check)); - #[hdl] - let out_firing_data = wire(); - connect(out_firing_data, HdlNone()); - #[hdl] - if out_index_matches { - connect(out_firing_data, ReadyValid::firing_data(dut.out)); - } - - // Implement a one-entry FIFO and ensure its equivalence to the - // filtered FIFO. - // - // the holding register for our one-entry FIFO - #[hdl] - let stored_reg = reg_builder().clock_domain(cd).reset(HdlNone()); - #[hdl] - match stored_reg { - // If the holding register is empty... - HdlNone => { #[hdl] - match inp_firing_data { - // ... and we are not receiving data, then we must not - // transmit any data. - HdlNone => hdl_assert(clk, HdlOption::is_none(out_firing_data), ""), - // If we are indeed receiving some data... - HdlSome(data_in) => { - #[hdl] - match out_firing_data { - // ... and transmitting at the same time, we - // must be transmitting the input data itself, - // since the holding register is empty. - HdlSome(data_out) => hdl_assert(clk, data_out.cmp_eq(data_in), ""), - // If we are receiving, but not transmitting, - // store the received data in the holding - // register. - HdlNone => connect(stored_reg, HdlSome(data_in)), - } - } - } - } - // If there is some value stored in the holding register... - HdlSome(stored) => { - #[hdl] - match out_firing_data { - // ... and we are not transmitting it, we cannot - // receive any more data. - HdlNone => hdl_assert(clk, HdlOption::is_none(inp_firing_data), ""), - // If we are transmitting a previously stored value... - HdlSome(data_out) => { - // ... it must be the same data we stored earlier. - hdl_assert(clk, data_out.cmp_eq(stored), ""); - // Also, accept new data, if any. Otherwise, - // let the holding register become empty. - connect(stored_reg, inp_firing_data); - } + if out_index_reg.cmp_eq(index_to_check) { + connect(stored_out_data_reg, data); } } } - // from now on, some extra assertions in order to pass induction - - // sync the holding register, when it's occupied, to the - // corresponding entry in the FIFO's circular buffer - connect(dut.dbg.index_to_check, index_to_check); #[hdl] - if let HdlSome(stored) = stored_reg { - hdl_assert(clk, stored.cmp_eq(dut.dbg.stored), ""); + if out_index_reg.cmp_lt(index_to_check) { + hdl_assert(clk, stored_out_data_reg.cmp_eq(0u8), ""); } - // sync the read and write indices - hdl_assert(clk, inp_index_reg.cmp_eq(dut.dbg.inp_index), ""); - hdl_assert(clk, out_index_reg.cmp_eq(dut.dbg.out_index), ""); + hdl_assert(clk, inp_index_reg.cmp_ge(out_index_reg), ""); - // the indices should never go past the capacity, but induction - // doesn't know that... - hdl_assert(clk, inp_index_reg.cmp_lt(capacity.get()), ""); - hdl_assert(clk, out_index_reg.cmp_lt(capacity.get()), ""); - - // strongly constrain the state of the holding register - // - // The holding register is full if and only if the corresponding - // FIFO entry was written to and not yet read. In other words, if - // the number of pending reads until the chosen entry is read out - // is greater than the current FIFO count, then the entry couldn't - // be in the FIFO in the first place. #[hdl] - let pending_reads: UInt = wire(index_ty); - // take care of wrap-around when subtracting indices, add the - // capacity amount to keep the result positive if necessary - #[hdl] - if index_to_check.cmp_ge(out_index_reg) { - connect(pending_reads, index_to_check - out_index_reg); + if inp_index_reg.cmp_lt(index_max) & out_index_reg.cmp_lt(index_max) { + hdl_assert( + clk, + expected_count_reg.cmp_eq(inp_index_reg - out_index_reg), + "", + ); } else { - connect( - pending_reads, - index_to_check + capacity.get() - out_index_reg, + hdl_assert( + clk, + expected_count_reg.cmp_ge(inp_index_reg - out_index_reg), + "", ); } - // check whether the chosen entry is in the FIFO + #[hdl] - let expected_stored: Bool = wire(); - connect(expected_stored, pending_reads.cmp_lt(dut.count)); - // sync with the state of the holding register - hdl_assert( - clk, - expected_stored.cmp_eq(HdlOption::is_some(stored_reg)), - "", - ); + if inp_index_reg.cmp_gt(index_to_check) & out_index_reg.cmp_gt(index_to_check) { + hdl_assert(clk, stored_inp_data_reg.cmp_eq(stored_out_data_reg), ""); + } } } @@ -543,24 +430,4 @@ mod tests { fn test_4_true_true() { test_queue(NonZero::new(4).unwrap(), true, true); } - - #[test] - fn test_many_false_false() { - test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, false); - } - - #[test] - fn test_many_false_true() { - test_queue(NonZero::new((2 << 16) - 5).unwrap(), false, true); - } - - #[test] - fn test_many_true_false() { - test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, false); - } - - #[test] - fn test_many_true_true() { - test_queue(NonZero::new((2 << 16) - 5).unwrap(), true, true); - } } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 4e56df4..4cb3057 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -4287,61 +4287,3 @@ circuit check_deduce_resets: ", }; } - -// intentionally not outline_generated to ensure we get correct macro hygiene -#[hdl_module] -pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A: Type, #[cfg(cfg_true_for_tests)] B: Type>( - #[cfg(cfg_false_for_tests)] a: A, - #[cfg(cfg_true_for_tests)] b: B, -) { - #[hdl] - struct S<#[cfg(cfg_false_for_tests)] A, #[cfg(cfg_true_for_tests)] B> { - #[cfg(cfg_false_for_tests)] - a: A, - #[cfg(cfg_true_for_tests)] - b: B, - } - #[hdl] - #[cfg(cfg_false_for_tests)] - let i_a: A = m.input(a); - #[hdl] - #[cfg(cfg_true_for_tests)] - let i_b: B = m.input(b); - #[hdl] - let w: S> = wire(); - #[cfg(cfg_false_for_tests)] - { - #[hdl] - let o_a: A = m.output(a); - connect(o_a, w.a.cast_bits_to(a)); - connect_any(w.a, i_a.cast_to_bits()); - } - #[cfg(cfg_true_for_tests)] - { - #[hdl] - let o_b: B = m.output(b); - connect(o_b, w.b.cast_bits_to(b)); - connect_any(w.b, i_b.cast_to_bits()); - } -} - -#[test] -fn test_cfgs() { - let _n = SourceLocation::normalize_files_for_tests(); - let m = check_cfgs(UInt[8]); - dbg!(m); - #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 - assert_export_firrtl! { - m => - "/test/check_cfgs.fir": r"FIRRTL version 3.2.0 -circuit check_cfgs: - type Ty0 = {b: UInt<8>} - module check_cfgs: @[the_test_file.rs 9962:1] - input i_b: UInt<8> @[the_test_file.rs 9979:20] - output o_b: UInt<8> @[the_test_file.rs 9992:24] - wire w: Ty0 @[the_test_file.rs 9981:25] - connect o_b, w.b @[the_test_file.rs 9993:9] - connect w.b, i_b @[the_test_file.rs 9994:9] -", - }; -} diff --git a/crates/fayalite/tests/sim.rs b/crates/fayalite/tests/sim.rs index 13e84eb..bea7d93 100644 --- a/crates/fayalite/tests/sim.rs +++ b/crates/fayalite/tests/sim.rs @@ -255,14 +255,8 @@ fn test_shift_register() { let mut sim = Simulation::new(shift_register()); let mut writer = RcWriter::default(); sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); - sim.write( - sim.io().cd, - #[hdl] - ClockDomain { - clk: false.to_clock(), - rst: true.to_sync_reset(), - }, - ); + sim.write_clock(sim.io().cd.clk, false); + sim.write_reset(sim.io().cd.rst, true); sim.write_bool(sim.io().d, false); sim.advance_time(SimDuration::from_micros(1)); sim.write_clock(sim.io().cd.clk, true); @@ -1246,33 +1240,3 @@ fn test_memories3() { panic!(); } } - -#[hdl_module(outline_generated)] -pub fn duplicate_names() { - #[hdl] - let w = wire(); - connect(w, 5u8); - #[hdl] - let w = wire(); - connect(w, 6u8); -} - -#[test] -fn test_duplicate_names() { - let _n = SourceLocation::normalize_files_for_tests(); - let mut sim = Simulation::new(duplicate_names()); - let mut writer = RcWriter::default(); - sim.add_trace_writer(VcdWriterDecls::new(writer.clone())); - sim.advance_time(SimDuration::from_micros(1)); - sim.flush_traces().unwrap(); - let vcd = String::from_utf8(writer.take()).unwrap(); - println!("####### VCD:\n{vcd}\n#######"); - if vcd != include_str!("sim/expected/duplicate_names.vcd") { - panic!(); - } - let sim_debug = format!("{sim:#?}"); - println!("#######\n{sim_debug}\n#######"); - if sim_debug != include_str!("sim/expected/duplicate_names.txt") { - panic!(); - } -} diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.txt b/crates/fayalite/tests/sim/expected/duplicate_names.txt deleted file mode 100644 index 8a59861..0000000 --- a/crates/fayalite/tests/sim/expected/duplicate_names.txt +++ /dev/null @@ -1,153 +0,0 @@ -Simulation { - state: State { - insns: Insns { - state_layout: StateLayout { - ty: TypeLayout { - small_slots: StatePartLayout { - len: 0, - debug_data: [], - .. - }, - big_slots: StatePartLayout { - len: 4, - debug_data: [ - SlotDebugData { - name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", - ty: UInt<8>, - }, - SlotDebugData { - name: "", - ty: UInt<8>, - }, - SlotDebugData { - name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", - ty: UInt<8>, - }, - SlotDebugData { - name: "", - ty: UInt<8>, - }, - ], - .. - }, - }, - memories: StatePartLayout { - len: 0, - debug_data: [], - layout_data: [], - .. - }, - }, - insns: [ - // at: module-XXXXXXXXXX.rs:1:1 - 0: Const { - dest: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, - value: 0x6, - }, - // at: module-XXXXXXXXXX.rs:5:1 - 1: Copy { - dest: StatePartIndex(2), // (0x6) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, - src: StatePartIndex(3), // (0x6) SlotDebugData { name: "", ty: UInt<8> }, - }, - // at: module-XXXXXXXXXX.rs:1:1 - 2: Const { - dest: StatePartIndex(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, - value: 0x5, - }, - // at: module-XXXXXXXXXX.rs:3:1 - 3: Copy { - dest: StatePartIndex(0), // (0x5) SlotDebugData { name: "InstantiatedModule(duplicate_names: duplicate_names).duplicate_names::w", ty: UInt<8> }, - src: StatePartIndex(1), // (0x5) SlotDebugData { name: "", ty: UInt<8> }, - }, - // at: module-XXXXXXXXXX.rs:1:1 - 4: Return, - ], - .. - }, - pc: 4, - memory_write_log: [], - memories: StatePart { - value: [], - }, - small_slots: StatePart { - value: [], - }, - big_slots: StatePart { - value: [ - 5, - 5, - 6, - 6, - ], - }, - }, - io: Instance { - name: ::duplicate_names, - instantiated: Module { - name: duplicate_names, - .. - }, - }, - uninitialized_inputs: {}, - io_targets: {}, - made_initial_step: true, - needs_settle: false, - trace_decls: TraceModule { - name: "duplicate_names", - children: [ - TraceWire { - name: "w", - child: TraceUInt { - location: TraceScalarId(0), - name: "w", - ty: UInt<8>, - flow: Duplex, - }, - ty: UInt<8>, - }, - TraceWire { - name: "w", - child: TraceUInt { - location: TraceScalarId(1), - name: "w", - ty: UInt<8>, - flow: Duplex, - }, - ty: UInt<8>, - }, - ], - }, - traces: [ - SimTrace { - id: TraceScalarId(0), - kind: BigUInt { - index: StatePartIndex(0), - ty: UInt<8>, - }, - state: 0x05, - last_state: 0x05, - }, - SimTrace { - id: TraceScalarId(1), - kind: BigUInt { - index: StatePartIndex(2), - ty: UInt<8>, - }, - state: 0x06, - last_state: 0x06, - }, - ], - trace_memories: {}, - trace_writers: [ - Running( - VcdWriter { - finished_init: true, - timescale: 1 ps, - .. - }, - ), - ], - instant: 1 μs, - clocks_triggered: [], - .. -} \ No newline at end of file diff --git a/crates/fayalite/tests/sim/expected/duplicate_names.vcd b/crates/fayalite/tests/sim/expected/duplicate_names.vcd deleted file mode 100644 index 1e9f6c6..0000000 --- a/crates/fayalite/tests/sim/expected/duplicate_names.vcd +++ /dev/null @@ -1,11 +0,0 @@ -$timescale 1 ps $end -$scope module duplicate_names $end -$var wire 8 ! w $end -$var wire 8 " w_2 $end -$upscope $end -$enddefinitions $end -$dumpvars -b101 ! -b110 " -$end -#1000000 diff --git a/crates/fayalite/tests/sim/expected/memories.vcd b/crates/fayalite/tests/sim/expected/memories.vcd index bedc354..72af410 100644 --- a/crates/fayalite/tests/sim/expected/memories.vcd +++ b/crates/fayalite/tests/sim/expected/memories.vcd @@ -24,97 +24,97 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct \[0] $end +$scope struct [0] $end $scope struct mem $end $var reg 8 9 \0 $end $var reg 8 I \1 $end $upscope $end $upscope $end -$scope struct \[1] $end +$scope struct [1] $end $scope struct mem $end $var reg 8 : \0 $end $var reg 8 J \1 $end $upscope $end $upscope $end -$scope struct \[2] $end +$scope struct [2] $end $scope struct mem $end $var reg 8 ; \0 $end $var reg 8 K \1 $end $upscope $end $upscope $end -$scope struct \[3] $end +$scope struct [3] $end $scope struct mem $end $var reg 8 < \0 $end $var reg 8 L \1 $end $upscope $end $upscope $end -$scope struct \[4] $end +$scope struct [4] $end $scope struct mem $end $var reg 8 = \0 $end $var reg 8 M \1 $end $upscope $end $upscope $end -$scope struct \[5] $end +$scope struct [5] $end $scope struct mem $end $var reg 8 > \0 $end $var reg 8 N \1 $end $upscope $end $upscope $end -$scope struct \[6] $end +$scope struct [6] $end $scope struct mem $end $var reg 8 ? \0 $end $var reg 8 O \1 $end $upscope $end $upscope $end -$scope struct \[7] $end +$scope struct [7] $end $scope struct mem $end $var reg 8 @ \0 $end $var reg 8 P \1 $end $upscope $end $upscope $end -$scope struct \[8] $end +$scope struct [8] $end $scope struct mem $end $var reg 8 A \0 $end $var reg 8 Q \1 $end $upscope $end $upscope $end -$scope struct \[9] $end +$scope struct [9] $end $scope struct mem $end $var reg 8 B \0 $end $var reg 8 R \1 $end $upscope $end $upscope $end -$scope struct \[10] $end +$scope struct [10] $end $scope struct mem $end $var reg 8 C \0 $end $var reg 8 S \1 $end $upscope $end $upscope $end -$scope struct \[11] $end +$scope struct [11] $end $scope struct mem $end $var reg 8 D \0 $end $var reg 8 T \1 $end $upscope $end $upscope $end -$scope struct \[12] $end +$scope struct [12] $end $scope struct mem $end $var reg 8 E \0 $end $var reg 8 U \1 $end $upscope $end $upscope $end -$scope struct \[13] $end +$scope struct [13] $end $scope struct mem $end $var reg 8 F \0 $end $var reg 8 V \1 $end $upscope $end $upscope $end -$scope struct \[14] $end +$scope struct [14] $end $scope struct mem $end $var reg 8 G \0 $end $var reg 8 W \1 $end $upscope $end $upscope $end -$scope struct \[15] $end +$scope struct [15] $end $scope struct mem $end $var reg 8 H \0 $end $var reg 8 X \1 $end diff --git a/crates/fayalite/tests/sim/expected/memories2.vcd b/crates/fayalite/tests/sim/expected/memories2.vcd index 4039754..bd48f24 100644 --- a/crates/fayalite/tests/sim/expected/memories2.vcd +++ b/crates/fayalite/tests/sim/expected/memories2.vcd @@ -11,31 +11,31 @@ $var wire 1 ' wmask $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct \[0] $end +$scope struct [0] $end $scope struct mem $end $var string 1 1 \$tag $end $var reg 1 6 HdlSome $end $upscope $end $upscope $end -$scope struct \[1] $end +$scope struct [1] $end $scope struct mem $end $var string 1 2 \$tag $end $var reg 1 7 HdlSome $end $upscope $end $upscope $end -$scope struct \[2] $end +$scope struct [2] $end $scope struct mem $end $var string 1 3 \$tag $end $var reg 1 8 HdlSome $end $upscope $end $upscope $end -$scope struct \[3] $end +$scope struct [3] $end $scope struct mem $end $var string 1 4 \$tag $end $var reg 1 9 HdlSome $end $upscope $end $upscope $end -$scope struct \[4] $end +$scope struct [4] $end $scope struct mem $end $var string 1 5 \$tag $end $var reg 1 : HdlSome $end diff --git a/crates/fayalite/tests/sim/expected/memories3.vcd b/crates/fayalite/tests/sim/expected/memories3.vcd index 5768560..328fcaa 100644 --- a/crates/fayalite/tests/sim/expected/memories3.vcd +++ b/crates/fayalite/tests/sim/expected/memories3.vcd @@ -42,7 +42,7 @@ $upscope $end $upscope $end $scope struct mem $end $scope struct contents $end -$scope struct \[0] $end +$scope struct [0] $end $scope struct mem $end $var reg 8 ] \[0] $end $var reg 8 e \[1] $end @@ -54,7 +54,7 @@ $var reg 8 /" \[6] $end $var reg 8 7" \[7] $end $upscope $end $upscope $end -$scope struct \[1] $end +$scope struct [1] $end $scope struct mem $end $var reg 8 ^ \[0] $end $var reg 8 f \[1] $end @@ -66,7 +66,7 @@ $var reg 8 0" \[6] $end $var reg 8 8" \[7] $end $upscope $end $upscope $end -$scope struct \[2] $end +$scope struct [2] $end $scope struct mem $end $var reg 8 _ \[0] $end $var reg 8 g \[1] $end @@ -78,7 +78,7 @@ $var reg 8 1" \[6] $end $var reg 8 9" \[7] $end $upscope $end $upscope $end -$scope struct \[3] $end +$scope struct [3] $end $scope struct mem $end $var reg 8 ` \[0] $end $var reg 8 h \[1] $end @@ -90,7 +90,7 @@ $var reg 8 2" \[6] $end $var reg 8 :" \[7] $end $upscope $end $upscope $end -$scope struct \[4] $end +$scope struct [4] $end $scope struct mem $end $var reg 8 a \[0] $end $var reg 8 i \[1] $end @@ -102,7 +102,7 @@ $var reg 8 3" \[6] $end $var reg 8 ;" \[7] $end $upscope $end $upscope $end -$scope struct \[5] $end +$scope struct [5] $end $scope struct mem $end $var reg 8 b \[0] $end $var reg 8 j \[1] $end @@ -114,7 +114,7 @@ $var reg 8 4" \[6] $end $var reg 8 <" \[7] $end $upscope $end $upscope $end -$scope struct \[6] $end +$scope struct [6] $end $scope struct mem $end $var reg 8 c \[0] $end $var reg 8 k \[1] $end @@ -126,7 +126,7 @@ $var reg 8 5" \[6] $end $var reg 8 =" \[7] $end $upscope $end $upscope $end -$scope struct \[7] $end +$scope struct [7] $end $scope struct mem $end $var reg 8 d \[0] $end $var reg 8 l \[1] $end