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