Compare commits

..

3 commits

Author SHA1 Message Date
Jacob Lifshay b63676d0ca
add test for cfgs
All checks were successful
/ deps (pull_request) Successful in 18s
/ test (pull_request) Successful in 5m23s
/ deps (push) Successful in 14s
/ test (push) Successful in 6m5s
2024-12-28 23:39:50 -08:00
Jacob Lifshay 7005fa3330
implement handling #[cfg] and #[cfg_attr] in proc macro inputs 2024-12-28 23:39:08 -08:00
Jacob Lifshay 2ab8428062
upgrade syn version 2024-12-28 23:39:08 -08:00
8 changed files with 1530 additions and 711 deletions

8
Cargo.lock generated
View file

@ -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",

View file

@ -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"

View file

@ -66,6 +66,7 @@ mod kw {
}; };
} }
custom_keyword!(__evaluated_cfgs);
custom_keyword!(all); custom_keyword!(all);
custom_keyword!(any); custom_keyword!(any);
custom_keyword!(cfg); custom_keyword!(cfg);
@ -1022,6 +1023,12 @@ pub(crate) struct Cfg {
trailing_comma: Option<Token![,]>, trailing_comma: Option<Token![,]>,
} }
impl Cfg {
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
}
impl ToTokens for Cfg { impl ToTokens for Cfg {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let Self { let Self {
@ -1068,6 +1075,9 @@ impl CfgAttr {
trailing_comma: None, trailing_comma: None,
} }
} }
fn parse_meta(meta: &Meta) -> syn::Result<Self> {
syn::parse2(meta.to_token_stream())
}
} }
impl Parse for CfgAttr { impl Parse for CfgAttr {
@ -1105,6 +1115,16 @@ pub(crate) struct Cfgs<T> {
pub(crate) cfgs_list: Vec<Cfg>, 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> { impl<T> Cfgs<T> {
fn insert_cfg(&mut self, cfg: Cfg, value: T) { fn insert_cfg(&mut self, cfg: Cfg, value: T) {
match self.cfgs_map.entry(cfg) { match self.cfgs_map.entry(cfg) {
@ -1125,10 +1145,11 @@ impl Parse for Cfgs<bool> {
let mut cfgs_list = Vec::new(); let mut cfgs_list = Vec::new();
for CfgAndValue { for CfgAndValue {
cfg, cfg,
eq_token: _, eq_token,
value, value,
} in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)? } in contents.call(Punctuated::<CfgAndValue, Token![,]>::parse_terminated)?
{ {
let _ = eq_token;
match cfgs_map.entry(cfg) { match cfgs_map.entry(cfg) {
Entry::Occupied(_) => {} Entry::Occupied(_) => {}
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
@ -1184,7 +1205,44 @@ impl ToTokens for Cfgs<()> {
} }
} }
fn handle_top_level_attr(item: Item) -> syn::Result<TokenStream> { 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),
@ -1197,39 +1255,10 @@ fn handle_top_level_attr(item: Item) -> syn::Result<TokenStream> {
} }
} }
fn generate_cfg_expansion_tokens(input: TokenStream) -> syn::Result<TokenStream> {
let item = syn::parse2::<Item>(input)?;
let cfgs = process_cfg::collect_cfgs(item.clone())?;
Ok(quote! {
::fayalite::__cfg_expansion_helper! {
[]
#cfgs
::fayalite::__after_cfg_expansion { #item }
}
})
}
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl_module::default(); hdl_main(kw::hdl_module::default(), attr, item)
generate_cfg_expansion_tokens(quote! { #[#kw(#attr)] #item })
} }
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl::default(); hdl_main(kw::hdl::default(), attr, item)
generate_cfg_expansion_tokens(quote! { #[#kw(#attr)] #item })
}
pub fn after_cfg_expansion(input: TokenStream) -> syn::Result<TokenStream> {
syn::parse::Parser::parse2(
|input: ParseStream| -> syn::Result<TokenStream> {
let cfgs = input.parse()?;
let item = input.parse()?;
let item = process_cfg::process_cfgs(item, cfgs)?;
Ok(item
.map(handle_top_level_attr)
.transpose()?
.unwrap_or_default())
},
input,
)
} }

File diff suppressed because it is too large Load diff

View file

@ -27,12 +27,3 @@ pub fn hdl(
Err(err) => err.into_compile_error().into(), Err(err) => err.into_compile_error().into(),
} }
} }
#[doc(hidden)]
#[proc_macro]
pub fn __after_cfg_expansion(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
match fayalite_proc_macros_impl::after_cfg_expansion(input.into()) {
Ok(retval) => retval.into(),
Err(err) => err.into_compile_error().into(),
}
}

View file

@ -6,6 +6,7 @@ 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_false_for_tests)");
println!("cargo::rustc-check-cfg=cfg(cfg_true_for_tests)");
println!("cargo::rustc-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}");

View file

@ -22,7 +22,7 @@ macro_rules! __cfg_expansion_helper {
$cfg:ident($($expr:tt)*), $cfg:ident($($expr:tt)*),
$($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)* $($unevaluated_cfgs:ident($($unevaluated_exprs:tt)*),)*
] ]
$after_evaluation:path {$($after_evaluation_args:tt)*} #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*}
) => { ) => {
#[$cfg($($expr)*)] #[$cfg($($expr)*)]
$crate::__cfg_expansion_helper! { $crate::__cfg_expansion_helper! {
@ -33,7 +33,7 @@ macro_rules! __cfg_expansion_helper {
[ [
$($unevaluated_cfgs($($unevaluated_exprs)*),)* $($unevaluated_cfgs($($unevaluated_exprs)*),)*
] ]
$after_evaluation {$($after_evaluation_args)*} #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*}
} }
#[$cfg(not($($expr)*))] #[$cfg(not($($expr)*))]
$crate::__cfg_expansion_helper! { $crate::__cfg_expansion_helper! {
@ -44,7 +44,7 @@ macro_rules! __cfg_expansion_helper {
[ [
$($unevaluated_cfgs($($unevaluated_exprs)*),)* $($unevaluated_cfgs($($unevaluated_exprs)*),)*
] ]
$after_evaluation {$($after_evaluation_args)*} #[$after_evaluation:($($after_evaluation_attr_args)*)] {$($after_evaluation_args)*}
} }
}; };
( (
@ -52,14 +52,12 @@ macro_rules! __cfg_expansion_helper {
$($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)* $($evaluated_cfgs:ident($($evaluated_exprs:tt)*) = $evaluated_results:ident,)*
] ]
[] []
$after_evaluation:path {$($after_evaluation_args:tt)*} #[$after_evaluation:path:($($after_evaluation_attr_args:tt)*)] {$($after_evaluation_args:tt)*}
) => { ) => {
$after_evaluation! { #[$after_evaluation([
[ $($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)*
$($evaluated_cfgs($($evaluated_exprs)*) = $evaluated_results,)* ] $($after_evaluation_attr_args)*)]
] $($after_evaluation_args)*
$($after_evaluation_args)*
}
}; };
} }
@ -75,9 +73,6 @@ pub use fayalite_proc_macros::hdl_module;
#[doc(inline)] #[doc(inline)]
pub use fayalite_proc_macros::hdl; pub use fayalite_proc_macros::hdl;
#[doc(hidden)]
pub use fayalite_proc_macros::__after_cfg_expansion;
/// struct used as a placeholder when applying defaults /// struct used as a placeholder when applying defaults
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct __; pub struct __;

View file

@ -4289,7 +4289,7 @@ circuit check_deduce_resets:
} }
#[hdl_module(outline_generated)] #[hdl_module(outline_generated)]
pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A, #[cfg(cfg_true_for_tests)] B>( 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_false_for_tests)] a: A,
#[cfg(cfg_true_for_tests)] b: B, #[cfg(cfg_true_for_tests)] b: B,
) { ) {
@ -4313,52 +4313,34 @@ pub fn check_cfgs<#[cfg(cfg_false_for_tests)] A, #[cfg(cfg_true_for_tests)] B>(
#[hdl] #[hdl]
let o_a: A = m.output(a); let o_a: A = m.output(a);
connect(o_a, w.a.cast_bits_to(a)); connect(o_a, w.a.cast_bits_to(a));
connect(w.a, i_a.cast_to_bits(UInt::new_static())); connect_any(w.a, i_a.cast_to_bits());
} }
#[cfg(cfg_true_for_tests)] #[cfg(cfg_true_for_tests)]
{ {
#[hdl] #[hdl]
let o_a: B = m.output(b); let o_b: B = m.output(b);
connect(o_b, w.b.cast_bits_to(b)); connect(o_b, w.b.cast_bits_to(b));
connect(w.b, i_b.cast_to_bits(UInt::new_static())); connect_any(w.b, i_b.cast_to_bits());
} }
} }
#[test] #[test]
fn test_cfgs() { fn test_cfgs() {
let _n = SourceLocation::normalize_files_for_tests(); let _n = SourceLocation::normalize_files_for_tests();
let m = check_cfgs(UInt::<8>); let m = check_cfgs(UInt[8]);
dbg!(m); dbg!(m);
#[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161 #[rustfmt::skip] // work around https://github.com/rust-lang/rustfmt/issues/6161
assert_export_firrtl! { assert_export_firrtl! {
m => m =>
options: ExportOptions { "/test/check_cfgs.fir": r"FIRRTL version 3.2.0
simplify_enums: None, circuit check_cfgs:
..ExportOptions::default() type Ty0 = {b: UInt<8>}
}, module check_cfgs: @[module-XXXXXXXXXX.rs 1:1]
"/test/check_deduce_resets.fir": r"FIRRTL version 3.2.0 input i_b: UInt<8> @[module-XXXXXXXXXX.rs 2:1]
circuit check_deduce_resets: output o_b: UInt<8> @[module-XXXXXXXXXX.rs 4:1]
type Ty0 = {clk: Clock, rst: Reset} wire w: Ty0 @[module-XXXXXXXXXX.rs 3:1]
type Ty1 = {|A: Reset, B: AsyncReset, C: UInt<1>|} connect o_b, w.b @[module-XXXXXXXXXX.rs 5:1]
module check_deduce_resets: @[module-XXXXXXXXXX.rs 1:1] connect w.b, i_b @[module-XXXXXXXXXX.rs 6:1]
input cd: Ty0 @[module-XXXXXXXXXX.rs 2:1]
input u8_in: UInt<8> @[module-XXXXXXXXXX.rs 4:1]
output u8_out: UInt<8> @[module-XXXXXXXXXX.rs 6:1]
input enum_in: Ty1 @[module-XXXXXXXXXX.rs 8:1]
output enum_out: Ty1 @[module-XXXXXXXXXX.rs 9:1]
output reset_out: Reset @[module-XXXXXXXXXX.rs 10:1]
regreset my_reg: UInt<8>, cd.clk, cd.rst, UInt<8>(0h0) @[module-XXXXXXXXXX.rs 3:1]
connect my_reg, u8_in @[module-XXXXXXXXXX.rs 5:1]
connect u8_out, my_reg @[module-XXXXXXXXXX.rs 7:1]
connect reset_out, cd.rst @[module-XXXXXXXXXX.rs 11:1]
match enum_in: @[module-XXXXXXXXXX.rs 12:1]
A(_match_arm_value):
connect enum_out, {|A: Reset, B: AsyncReset, C: UInt<1>|}(A, cd.rst) @[module-XXXXXXXXXX.rs 13:1]
connect reset_out, _match_arm_value @[module-XXXXXXXXXX.rs 14:1]
B(_match_arm_value_1):
connect enum_out, {|A: Reset, B: AsyncReset, C: UInt<1>|}(B, _match_arm_value_1) @[module-XXXXXXXXXX.rs 15:1]
C(_match_arm_value_2):
connect enum_out, {|A: Reset, B: AsyncReset, C: UInt<1>|}(C, _match_arm_value_2) @[module-XXXXXXXXXX.rs 16:1]
", ",
}; };
} }