implementing handling #[cfg] and #[cfg_attr] in proc macro inputs #12
					 7 changed files with 2994 additions and 16 deletions
				
			
		
							
								
								
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								Cargo.lock
									
										
									
										generated
									
									
									
								
							|  | @ -543,9 +543,9 @@ dependencies = [ | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "proc-macro2" | name = "proc-macro2" | ||||||
| version = "1.0.83" | version = "1.0.92" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "unicode-ident", |  "unicode-ident", | ||||||
| ] | ] | ||||||
|  | @ -647,9 +647,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" | ||||||
| 
 | 
 | ||||||
| [[package]] | [[package]] | ||||||
| name = "syn" | name = "syn" | ||||||
| version = "2.0.66" | version = "2.0.93" | ||||||
| source = "registry+https://github.com/rust-lang/crates.io-index" | source = "registry+https://github.com/rust-lang/crates.io-index" | ||||||
| checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" | checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" | ||||||
| dependencies = [ | dependencies = [ | ||||||
|  "proc-macro2", |  "proc-macro2", | ||||||
|  "quote", |  "quote", | ||||||
|  |  | ||||||
|  | @ -37,7 +37,7 @@ quote = "1.0.36" | ||||||
| serde = { version = "1.0.202", features = ["derive"] } | serde = { version = "1.0.202", features = ["derive"] } | ||||||
| serde_json = { version = "1.0.117", features = ["preserve_order"] } | serde_json = { version = "1.0.117", features = ["preserve_order"] } | ||||||
| sha2 = "0.10.8" | sha2 = "0.10.8" | ||||||
| syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"] } | syn = { version = "2.0.93", features = ["full", "fold", "visit", "extra-traits"] } | ||||||
| tempfile = "3.10.1" | tempfile = "3.10.1" | ||||||
| thiserror = "1.0.61" | thiserror = "1.0.61" | ||||||
| trybuild = "1.0" | trybuild = "1.0" | ||||||
|  |  | ||||||
|  | @ -3,14 +3,20 @@ | ||||||
| #![cfg_attr(test, recursion_limit = "512")] | #![cfg_attr(test, recursion_limit = "512")] | ||||||
| use proc_macro2::{Span, TokenStream}; | use proc_macro2::{Span, TokenStream}; | ||||||
| use quote::{quote, ToTokens}; | use quote::{quote, ToTokens}; | ||||||
| use std::io::{ErrorKind, Write}; | use std::{ | ||||||
|  |     collections::{hash_map::Entry, HashMap}, | ||||||
|  |     io::{ErrorKind, Write}, | ||||||
|  | }; | ||||||
| use syn::{ | use syn::{ | ||||||
|     bracketed, parenthesized, |     bracketed, | ||||||
|  |     ext::IdentExt, | ||||||
|  |     parenthesized, | ||||||
|     parse::{Parse, ParseStream, Parser}, |     parse::{Parse, ParseStream, Parser}, | ||||||
|     parse_quote, |     parse_quote, | ||||||
|     punctuated::Pair, |     punctuated::{Pair, Punctuated}, | ||||||
|     spanned::Spanned, |     spanned::Spanned, | ||||||
|     AttrStyle, Attribute, Error, Item, ItemFn, Token, |     token::{Bracket, Paren}, | ||||||
|  |     AttrStyle, Attribute, Error, Ident, Item, ItemFn, LitBool, LitStr, Meta, Token, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| mod fold; | mod fold; | ||||||
|  | @ -19,6 +25,7 @@ mod hdl_enum; | ||||||
| mod hdl_type_alias; | mod hdl_type_alias; | ||||||
| mod hdl_type_common; | mod hdl_type_common; | ||||||
| mod module; | mod module; | ||||||
|  | mod process_cfg; | ||||||
| 
 | 
 | ||||||
| pub(crate) trait CustomToken: | pub(crate) trait CustomToken: | ||||||
|     Copy |     Copy | ||||||
|  | @ -59,6 +66,11 @@ 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!(clock_domain); | ||||||
|     custom_keyword!(connect_inexact); |     custom_keyword!(connect_inexact); | ||||||
|     custom_keyword!(custom_bounds); |     custom_keyword!(custom_bounds); | ||||||
|  | @ -75,6 +87,7 @@ mod kw { | ||||||
|     custom_keyword!(no_reset); |     custom_keyword!(no_reset); | ||||||
|     custom_keyword!(no_runtime_generics); |     custom_keyword!(no_runtime_generics); | ||||||
|     custom_keyword!(no_static); |     custom_keyword!(no_static); | ||||||
|  |     custom_keyword!(not); | ||||||
|     custom_keyword!(outline_generated); |     custom_keyword!(outline_generated); | ||||||
|     custom_keyword!(output); |     custom_keyword!(output); | ||||||
|     custom_keyword!(reg_builder); |     custom_keyword!(reg_builder); | ||||||
|  | @ -901,15 +914,335 @@ fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> { | ||||||
|     Ok(contents) |     Ok(contents) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||||||
|     let kw = kw::hdl_module::default(); | pub(crate) enum CfgExpr { | ||||||
|     hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?) |     Option { | ||||||
|  |         ident: Ident, | ||||||
|  |         value: Option<(Token![=], LitStr)>, | ||||||
|  |     }, | ||||||
|  |     All { | ||||||
|  |         all: kw::all, | ||||||
|  |         paren: Paren, | ||||||
|  |         exprs: Punctuated<CfgExpr, Token![,]>, | ||||||
|  |     }, | ||||||
|  |     Any { | ||||||
|  |         any: kw::any, | ||||||
|  |         paren: Paren, | ||||||
|  |         exprs: Punctuated<CfgExpr, Token![,]>, | ||||||
|  |     }, | ||||||
|  |     Not { | ||||||
|  |         not: kw::not, | ||||||
|  |         paren: Paren, | ||||||
|  |         expr: Box<CfgExpr>, | ||||||
|  |         trailing_comma: Option<Token![,]>, | ||||||
|  |     }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | impl Parse for CfgExpr { | ||||||
|     let kw = kw::hdl::default(); |     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||||
|     let item = quote! { #[#kw(#attr)] #item }; |         match input.cursor().ident() { | ||||||
|     let item = syn::parse2::<Item>(item)?; |             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<Token![,]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Cfg { | ||||||
|  |     fn parse_meta(meta: &Meta) -> syn::Result<Self> { | ||||||
|  |         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<Self> { | ||||||
|  |         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<Meta, Token![,]>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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<Self> { | ||||||
|  |         syn::parse2(meta.to_token_stream()) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Parse for CfgAttr { | ||||||
|  |     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||||
|  |         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<Self> { | ||||||
|  |         Ok(Self { | ||||||
|  |             cfg: input.parse()?, | ||||||
|  |             eq_token: input.parse()?, | ||||||
|  |             value: input.parse()?, | ||||||
|  |         }) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub(crate) struct Cfgs<T> { | ||||||
|  |     pub(crate) bracket: Bracket, | ||||||
|  |     pub(crate) cfgs_map: HashMap<Cfg, T>, | ||||||
|  |     pub(crate) cfgs_list: Vec<Cfg>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> Default for Cfgs<T> { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         Self { | ||||||
|  |             bracket: Default::default(), | ||||||
|  |             cfgs_map: Default::default(), | ||||||
|  |             cfgs_list: Default::default(), | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<T> Cfgs<T> { | ||||||
|  |     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<bool> { | ||||||
|  |     fn parse(input: ParseStream) -> syn::Result<Self> { | ||||||
|  |         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::<CfgAndValue, Token![,]>::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<Self> { | ||||||
|  |         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::<Cfg, Token![,]>::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); | ||||||
|  |                 <Token![,]>::default().to_tokens(tokens); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn hdl_main( | ||||||
|  |     kw: impl CustomToken, | ||||||
|  |     attr: TokenStream, | ||||||
|  |     item: TokenStream, | ||||||
|  | ) -> syn::Result<TokenStream> { | ||||||
|  |     let (evaluated_cfgs, attr): (_, TokenStream) = Parser::parse2( | ||||||
|  |         |input: ParseStream| { | ||||||
|  |             if input.peek(Bracket) && input.peek2(kw::__evaluated_cfgs) { | ||||||
|  |                 let cfgs = input.parse()?; | ||||||
|  |                 let _: kw::__evaluated_cfgs = input.parse()?; | ||||||
|  |                 Ok((Some(cfgs), input.parse()?)) | ||||||
|  |             } else { | ||||||
|  |                 Ok((None, input.parse()?)) | ||||||
|  |             } | ||||||
|  |         }, | ||||||
|  |         attr, | ||||||
|  |     )?; | ||||||
|  |     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 { | ||||||
|  |             let key = kw::__evaluated_cfgs::default(); | ||||||
|  |             return Ok(quote! { | ||||||
|  |                 ::fayalite::__cfg_expansion_helper! { | ||||||
|  |                     [] | ||||||
|  |                     #cfgs | ||||||
|  |                     #[::fayalite::#kw:(#key #attr)] { #item } | ||||||
|  |                 } | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     let item = syn::parse2(quote! { #[#kw(#attr)] #item })?; | ||||||
|  |     let Some(item) = process_cfg::process_cfgs(item, cfgs)? else { | ||||||
|  |         return Ok(TokenStream::new()); | ||||||
|  |     }; | ||||||
|     match item { |     match item { | ||||||
|         Item::Enum(item) => hdl_enum::hdl_enum(item), |         Item::Enum(item) => hdl_enum::hdl_enum(item), | ||||||
|         Item::Struct(item) => hdl_bundle::hdl_bundle(item), |         Item::Struct(item) => hdl_bundle::hdl_bundle(item), | ||||||
|  | @ -921,3 +1254,11 @@ pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream | ||||||
|         )), |         )), | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||||
|  |     hdl_main(kw::hdl_module::default(), attr, item) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { | ||||||
|  |     hdl_main(kw::hdl::default(), attr, item) | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										2527
									
								
								crates/fayalite-proc-macros-impl/src/process_cfg.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2527
									
								
								crates/fayalite-proc-macros-impl/src/process_cfg.rs
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							|  | @ -5,6 +5,9 @@ use std::{env, fs, path::Path}; | ||||||
| 
 | 
 | ||||||
| fn main() { | fn main() { | ||||||
|     println!("cargo::rustc-check-cfg=cfg(todo)"); |     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"; |     let path = "visit_types.json"; | ||||||
|     println!("cargo::rerun-if-changed={path}"); |     println!("cargo::rerun-if-changed={path}"); | ||||||
|     println!("cargo::rerun-if-changed=build.rs"); |     println!("cargo::rerun-if-changed=build.rs"); | ||||||
|  |  | ||||||
|  | @ -11,6 +11,56 @@ extern crate self as fayalite; | ||||||
| #[doc(hidden)] | #[doc(hidden)] | ||||||
| pub use std as __std; | 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)*),)* | ||||||
|  |         ] | ||||||
|  |         #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} | ||||||
|  |     ) => { | ||||||
|  |         #[$cfg($($expr)*)] | ||||||
|  |         $crate::__cfg_expansion_helper! { | ||||||
|  |             [ | ||||||
|  |                 $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||||
|  |                 $cfg($($expr)*) = true, | ||||||
|  |             ] | ||||||
|  |             [ | ||||||
|  |                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||||
|  |             ] | ||||||
|  |             #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} | ||||||
|  |         } | ||||||
|  |         #[$cfg(not($($expr)*))] | ||||||
|  |         $crate::__cfg_expansion_helper! { | ||||||
|  |             [ | ||||||
|  |                 $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||||
|  |                 $cfg($($expr)*) = false, | ||||||
|  |             ] | ||||||
|  |             [ | ||||||
|  |                 $($unevaluated_cfgs($($unevaluated_exprs)*),)* | ||||||
|  |             ] | ||||||
|  |             #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*} | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     ( | ||||||
|  |         [ | ||||||
|  |             $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* | ||||||
|  |         ] | ||||||
|  |         [] | ||||||
|  |         #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*} | ||||||
|  |     ) => { | ||||||
|  |         #[$after_evaluation([ | ||||||
|  |             $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* | ||||||
|  |         ] $($after_evaluation_attr_args)*)] | ||||||
|  |         $($after_evaluation_args)* | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| #[doc(inline)] | #[doc(inline)] | ||||||
| /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | /// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
 | ||||||
| /// a [`Module`][`::fayalite::module::Module`] when called.
 | /// a [`Module`][`::fayalite::module::Module`] when called.
 | ||||||
|  |  | ||||||
|  | @ -4287,3 +4287,60 @@ circuit check_deduce_resets: | ||||||
| ",
 | ",
 | ||||||
|     }; |     }; | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | #[hdl_module(outline_generated)] | ||||||
|  | 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<UInt<8>> = 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: @[module-XXXXXXXXXX.rs 1:1] | ||||||
|  |         input i_b: UInt<8> @[module-XXXXXXXXXX.rs 2:1] | ||||||
|  |         output o_b: UInt<8> @[module-XXXXXXXXXX.rs 4:1] | ||||||
|  |         wire w: Ty0 @[module-XXXXXXXXXX.rs 3:1] | ||||||
|  |         connect o_b, w.b @[module-XXXXXXXXXX.rs 5:1] | ||||||
|  |         connect w.b, i_b @[module-XXXXXXXXXX.rs 6:1] | ||||||
|  | ",
 | ||||||
|  |     }; | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue