implementing handling #[cfg] and #[cfg_attr] in proc macro inputs #12
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…
Reference in a new issue