diff --git a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs index f29f43a..7f7d626 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_bundle.rs @@ -19,13 +19,13 @@ use syn::{ #[derive(Clone, Debug)] pub(crate) struct ParsedBundle { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) struct_token: Token![struct], pub(crate) ident: Ident, pub(crate) generics: MaybeParsed, pub(crate) fields: MaybeParsed, - pub(crate) field_flips: Vec>>, + pub(crate) field_flips: Vec>>, pub(crate) mask_type_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident, pub(crate) match_variant_ident: Ident, @@ -38,7 +38,7 @@ impl ParsedBundle { errors: &mut Errors, field: &mut Field, index: usize, - ) -> Option> { + ) -> Option> { let Field { attrs, vis: _, @@ -71,7 +71,9 @@ impl ParsedBundle { } = item; let mut errors = Errors::new(); let mut options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(&mut attrs)) + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) .unwrap_or_default(); errors.ok(options.body.validate()); let ItemOptions { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs index 1e0b66b..d7e5b61 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_enum.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_enum.rs @@ -3,7 +3,7 @@ use crate::{ common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, SplitForImpl, TypesParser, WrappedInConst, }, - Errors, HdlAttr, PairsIterExt, + kw, Errors, HdlAttr, PairsIterExt, }; use proc_macro2::TokenStream; use quote::{format_ident, quote_spanned, ToTokens}; @@ -29,7 +29,7 @@ crate::options! { pub(crate) struct ParsedVariantField { pub(crate) paren_token: Paren, pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ty: MaybeParsed, pub(crate) comma_token: Option, } @@ -37,7 +37,7 @@ pub(crate) struct ParsedVariantField { #[derive(Clone, Debug)] pub(crate) struct ParsedVariant { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) field: Option, } @@ -119,7 +119,7 @@ impl ParsedVariant { #[derive(Clone, Debug)] pub(crate) struct ParsedEnum { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) vis: Visibility, pub(crate) enum_token: Token![enum], pub(crate) ident: Ident, @@ -142,7 +142,9 @@ impl ParsedEnum { } = item; let mut errors = Errors::new(); let mut options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(&mut attrs)) + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) .unwrap_or_default(); errors.ok(options.body.validate()); let ItemOptions { diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs index 88da9a7..e33d0e0 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -1745,7 +1745,7 @@ impl, I, P: Clone> ParseTypes> for Punctuated< pub(crate) enum UnparsedGenericParam { Type { attrs: Vec, - options: HdlAttr, + options: HdlAttr, ident: Ident, colon_token: Token![:], bounds: ParsedBounds, @@ -1753,7 +1753,7 @@ pub(crate) enum UnparsedGenericParam { }, Const { attrs: Vec, - options: HdlAttr, + options: HdlAttr, const_token: Token![const], ident: Ident, colon_token: Token![:], @@ -2278,7 +2278,7 @@ impl ParsedBound { #[derive(Debug, Clone)] pub(crate) struct ParsedTypeParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) colon_token: Token![:], pub(crate) bounds: ParsedTypeBounds, @@ -2312,7 +2312,7 @@ impl ToTokens for ParsedTypeParam { #[derive(Debug, Clone)] pub(crate) struct ParsedSizeTypeParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) ident: Ident, pub(crate) colon_token: Token![:], pub(crate) bounds: ParsedSizeTypeBounds, @@ -2356,7 +2356,7 @@ pub(crate) struct ParsedConstParamWhereBounds { #[derive(Debug, Clone)] pub(crate) struct ParsedConstParam { pub(crate) attrs: Vec, - pub(crate) options: HdlAttr, + pub(crate) options: HdlAttr, pub(crate) const_token: Token![const], pub(crate) ident: Ident, pub(crate) colon_token: Token![:], @@ -2413,7 +2413,7 @@ impl ParsedGenericParam { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub(crate) struct ParsedGenerics { pub(crate) lt_token: Option, pub(crate) params: Punctuated, @@ -2863,9 +2863,11 @@ impl ParsedGenerics { let (input_param, punct) = input_param.into_tuple(); let (unparsed_param, late_parsed_param) = match input_param { GenericParam::Lifetime(param) => { - errors.unwrap_or_default(HdlAttr::::parse_and_take_attr( - &mut param.attrs, - )); + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr( + &mut param.attrs, + ), + ); errors.error(param, "lifetime generics are not supported by #[hdl]"); continue; } @@ -2879,7 +2881,9 @@ impl ParsedGenerics { }) => { let span = ident.span(); let options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .unwrap_or_default(); let colon_token = colon_token.unwrap_or_else(|| Token![:](span)); if !bounds.is_empty() { @@ -2917,7 +2921,9 @@ impl ParsedGenerics { default, }) => { let options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .unwrap_or_default(); if let Some(default) = default { let _ = eq_token; diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 94fb040..3ec00bf 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -9,7 +9,8 @@ use syn::{ parse::{Parse, ParseStream, Parser}, parse_quote, punctuated::Pair, - AttrStyle, Attribute, Error, Item, Token, + spanned::Spanned, + AttrStyle, Attribute, Error, Item, ItemFn, Token, }; mod fold; @@ -17,8 +18,20 @@ mod hdl_bundle; mod hdl_enum; mod hdl_type_common; mod module; -//mod value_derive_common; -//mod value_derive_struct; + +pub(crate) trait CustomToken: + Copy + + Spanned + + ToTokens + + std::fmt::Debug + + Eq + + std::hash::Hash + + Default + + quote::IdentFragment + + Parse +{ + const IDENT_STR: &'static str; +} mod kw { pub(crate) use syn::token::Extern as extern_; @@ -38,6 +51,10 @@ mod kw { } crate::fold::no_op_fold!($kw); + + impl crate::CustomToken for $kw { + const IDENT_STR: &'static str = stringify!($kw); + } }; } @@ -46,6 +63,7 @@ mod kw { custom_keyword!(custom_bounds); custom_keyword!(flip); custom_keyword!(hdl); + custom_keyword!(hdl_module); custom_keyword!(input); custom_keyword!(instance); custom_keyword!(m); @@ -68,34 +86,34 @@ mod kw { type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676 #[derive(Clone, Debug)] -pub(crate) struct HdlAttr { +pub(crate) struct HdlAttr { pub(crate) pound_token: Pound, pub(crate) style: AttrStyle, pub(crate) bracket_token: syn::token::Bracket, - pub(crate) hdl: kw::hdl, + pub(crate) kw: KW, pub(crate) paren_token: Option, pub(crate) body: T, } crate::fold::impl_fold! { - struct HdlAttr { + struct HdlAttr { pound_token: Pound, style: AttrStyle, bracket_token: syn::token::Bracket, - hdl: kw::hdl, + kw: KW, paren_token: Option, body: T, } } #[allow(dead_code)] -impl HdlAttr { - pub(crate) fn split_body(self) -> (HdlAttr<()>, T) { +impl HdlAttr { + pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -104,19 +122,19 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: (), }, body, ) } - pub(crate) fn replace_body(self, body: T2) -> HdlAttr { + pub(crate) fn replace_body(self, body: T2) -> HdlAttr { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: _, } = self; @@ -124,17 +142,20 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } } - pub(crate) fn as_ref(&self) -> HdlAttr<&T> { + pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW> + where + KW: Clone, + { let Self { pound_token, style, bracket_token, - hdl, + ref kw, paren_token, ref body, } = *self; @@ -142,17 +163,20 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw: kw.clone(), paren_token, body, } } - pub(crate) fn try_map Result>(self, f: F) -> Result, E> { + pub(crate) fn try_map Result>( + self, + f: F, + ) -> Result, E> { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -160,17 +184,17 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: f(body)?, }) } - pub(crate) fn map R>(self, f: F) -> HdlAttr { + pub(crate) fn map R>(self, f: F) -> HdlAttr { let Self { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, } = self; @@ -178,7 +202,7 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body: f(body), } @@ -186,31 +210,32 @@ impl HdlAttr { fn to_attr(&self) -> Attribute where T: ToTokens, + KW: ToTokens, { parse_quote! { #self } } } -impl Default for HdlAttr { +impl Default for HdlAttr { fn default() -> Self { T::default().into() } } -impl From for HdlAttr { +impl From for HdlAttr { fn from(body: T) -> Self { HdlAttr { pound_token: Default::default(), style: AttrStyle::Outer, bracket_token: Default::default(), - hdl: Default::default(), + kw: Default::default(), paren_token: Default::default(), body, } } } -impl ToTokens for HdlAttr { +impl ToTokens for HdlAttr { fn to_tokens(&self, tokens: &mut TokenStream) { self.pound_token.to_tokens(tokens); match self.style { @@ -218,7 +243,7 @@ impl ToTokens for HdlAttr { AttrStyle::Outer => {} }; self.bracket_token.surround(tokens, |tokens| { - self.hdl.to_tokens(tokens); + self.kw.to_tokens(tokens); match self.paren_token { Some(paren_token) => { paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens)) @@ -226,7 +251,7 @@ impl ToTokens for HdlAttr { None => { let body = self.body.to_token_stream(); if !body.is_empty() { - syn::token::Paren(self.hdl.span) + syn::token::Paren(self.kw.span()) .surround(tokens, |tokens| tokens.extend([body])); } } @@ -235,18 +260,24 @@ impl ToTokens for HdlAttr { } } -fn is_hdl_attr(attr: &Attribute) -> bool { - attr.path().is_ident("hdl") +fn is_hdl_attr(attr: &Attribute) -> bool { + attr.path().is_ident(KW::IDENT_STR) } -impl HdlAttr { - fn parse_and_take_attr(attrs: &mut Vec) -> syn::Result> { +impl HdlAttr { + fn parse_and_take_attr(attrs: &mut Vec) -> syn::Result> + where + KW: ToTokens, + { let mut retval = None; let mut errors = Errors::new(); attrs.retain(|attr| { - if is_hdl_attr(attr) { + if let Ok(kw) = syn::parse2::(attr.path().to_token_stream()) { if retval.is_some() { - errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); + errors.push(Error::new_spanned( + attr, + format_args!("more than one #[{}] attribute", kw.to_token_stream()), + )); } errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); false @@ -257,13 +288,19 @@ impl HdlAttr { errors.finish()?; Ok(retval) } - fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result> { + fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result> + where + KW: ToTokens, + { let mut retval = None; let mut errors = Errors::new(); for attr in attrs { - if is_hdl_attr(attr) { + if let Ok(kw) = syn::parse2::(attr.path().to_token_stream()) { if retval.is_some() { - errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); + errors.push(Error::new_spanned( + attr, + format_args!("more than one #[{}] attribute", kw.to_token_stream()), + )); } errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); } @@ -284,7 +321,7 @@ impl HdlAttr { ) -> syn::Result { let bracket_content; let bracket_token = bracketed!(bracket_content in input); - let hdl = bracket_content.parse()?; + let kw = bracket_content.parse()?; let paren_content; let body; let paren_token; @@ -305,7 +342,7 @@ impl HdlAttr { pound_token, style, bracket_token, - hdl, + kw, paren_token, body, }) @@ -852,25 +889,31 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr } } -pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { - let options = syn::parse2::(attr)?; - let options = HdlAttr::from(options); - let func = syn::parse2::(quote! { #options #item })?; +fn hdl_module_impl(item: ItemFn) -> syn::Result { + let func = module::ModuleFn::parse_from_fn(item)?; + let options = func.config_options(); let mut contents = func.generate(); - if options.body.outline_generated.is_some() { + if options.outline_generated.is_some() { contents = outline_generated(contents, "module-"); } Ok(contents) } +pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result { + let kw = kw::hdl_module::default(); + hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?) +} + pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result { - let item = syn::parse2::(quote! { #[hdl(#attr)] #item })?; + let kw = kw::hdl::default(); + let item = syn::parse2::(quote! { #[#kw(#attr)] #item })?; match item { Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item), + Item::Fn(item) => hdl_module_impl(item), _ => Err(syn::Error::new( Span::call_site(), - "top-level #[hdl] can only be used on structs or enums", + "top-level #[hdl] can only be used on structs, enums, or functions", )), } } diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 0945abb..6363bb3 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ hdl_type_common::{ParsedGenerics, SplitForImpl}, + kw, module::transform_body::{HdlLet, HdlLetKindIO}, options, Errors, HdlAttr, PairsIterExt, }; @@ -9,7 +10,6 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::collections::HashSet; use syn::{ - parse::{Parse, ParseStream}, parse_quote, visit::{visit_pat, Visit}, Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, @@ -59,9 +59,9 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> { pub(crate) type ModuleIO = HdlLet; -pub(crate) struct ModuleFn { +struct ModuleFnModule { attrs: Vec, - config_options: HdlAttr, + config_options: HdlAttr, module_kind: ModuleKind, vis: Visibility, sig: Signature, @@ -70,6 +70,26 @@ pub(crate) struct ModuleFn { the_struct: TokenStream, } +enum ModuleFnImpl { + Module(ModuleFnModule), + Fn { + attrs: Vec, + config_options: HdlAttr, + vis: Visibility, + sig: Signature, + block: Box, + }, +} + +options! { + pub(crate) enum HdlOrHdlModule { + Hdl(hdl), + HdlModule(hdl_module), + } +} + +pub(crate) struct ModuleFn(ModuleFnImpl); + #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) enum ModuleKind { Extern, @@ -89,14 +109,25 @@ impl Visit<'_> for ContainsSkippedIdent<'_> { } } -impl Parse for ModuleFn { - fn parse(input: ParseStream) -> syn::Result { +impl ModuleFn { + pub(crate) fn config_options(&self) -> ConfigOptions { + let (ModuleFnImpl::Module(ModuleFnModule { + config_options: HdlAttr { body, .. }, + .. + }) + | ModuleFnImpl::Fn { + config_options: HdlAttr { body, .. }, + .. + }) = &self.0; + body.clone() + } + pub(crate) fn parse_from_fn(item: ItemFn) -> syn::Result { let ItemFn { mut attrs, vis, mut sig, block, - } = input.parse()?; + } = item; let Signature { ref constness, ref asyncness, @@ -111,43 +142,60 @@ impl Parse for ModuleFn { ref output, } = sig; let mut errors = Errors::new(); - let config_options = errors - .unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) - .unwrap_or_default(); + let Some(mut config_options) = + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr(&mut attrs), + ) + else { + errors.error(sig.ident, "missing #[hdl] or #[hdl_module] attribute"); + errors.finish()?; + unreachable!(); + }; let ConfigOptions { outline_generated: _, extern_, } = config_options.body; - let module_kind = match extern_ { - Some(_) => ModuleKind::Extern, - None => ModuleKind::Normal, + let module_kind = match (config_options.kw, extern_) { + (HdlOrHdlModule::Hdl(_), None) => None, + (HdlOrHdlModule::Hdl(_), Some(extern2)) => { + config_options.body.extern_ = None; + errors.error( + extern2.0, + "extern can only be used as #[hdl_module(extern)]", + ); + None + } + (HdlOrHdlModule::HdlModule(_), None) => Some(ModuleKind::Normal), + (HdlOrHdlModule::HdlModule(_), Some(_)) => Some(ModuleKind::Extern), }; - for fn_arg in inputs { - match fn_arg { - FnArg::Receiver(_) => { - errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here")); - } - FnArg::Typed(fn_arg) => { - visit_pat( - &mut CheckNameConflictsWithModuleBuilderVisitor { - errors: &mut errors, - }, - &fn_arg.pat, - ); + if let HdlOrHdlModule::HdlModule(_) = config_options.kw { + for fn_arg in inputs { + match fn_arg { + FnArg::Receiver(_) => { + errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here")); + } + FnArg::Typed(fn_arg) => { + visit_pat( + &mut CheckNameConflictsWithModuleBuilderVisitor { + errors: &mut errors, + }, + &fn_arg.pat, + ); + } } } - } - if let Some(constness) = constness { - errors.push(syn::Error::new_spanned(constness, "const not allowed here")); - } - if let Some(asyncness) = asyncness { - errors.push(syn::Error::new_spanned(asyncness, "async not allowed here")); - } - if let Some(unsafety) = unsafety { - errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here")); - } - if let Some(abi) = abi { - errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); + if let Some(constness) = constness { + errors.push(syn::Error::new_spanned(constness, "const not allowed here")); + } + if let Some(asyncness) = asyncness { + errors.push(syn::Error::new_spanned(asyncness, "async not allowed here")); + } + if let Some(unsafety) = unsafety { + errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here")); + } + if let Some(abi) = abi { + errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); + } } let mut skipped_idents = HashSet::new(); let struct_generic_params = generics @@ -155,14 +203,17 @@ impl Parse for ModuleFn { .pairs_mut() .filter_map_pair_value_mut(|v| match v { GenericParam::Lifetime(LifetimeParam { attrs, .. }) => { - errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)); + errors.unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ); None } GenericParam::Type(TypeParam { attrs, ident, .. }) | GenericParam::Const(ConstParam { attrs, ident, .. }) => { if errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default( + HdlAttr::::parse_and_take_attr(attrs), + ) .is_some() { skipped_idents.insert(ident.clone()); @@ -176,6 +227,7 @@ impl Parse for ModuleFn { let struct_where_clause = generics .where_clause .as_mut() + .filter(|_| matches!(config_options.kw, HdlOrHdlModule::HdlModule(_))) .map(|where_clause| WhereClause { where_token: where_clause.where_token, predicates: where_clause @@ -198,22 +250,26 @@ impl Parse for ModuleFn { }) .collect(), }); - let struct_generics = Generics { - lt_token: generics.lt_token, - params: struct_generic_params, - gt_token: generics.gt_token, - where_clause: struct_where_clause, + let struct_generics = if let HdlOrHdlModule::HdlModule(_) = config_options.kw { + let mut struct_generics = Generics { + lt_token: generics.lt_token, + params: struct_generic_params, + gt_token: generics.gt_token, + where_clause: struct_where_clause, + }; + if let Some(variadic) = variadic { + errors.push(syn::Error::new_spanned(variadic, "... not allowed here")); + } + if !matches!(output, ReturnType::Default) { + errors.push(syn::Error::new_spanned( + output, + "return type not allowed here", + )); + } + errors.ok(ParsedGenerics::parse(&mut struct_generics)) + } else { + Some(ParsedGenerics::default()) }; - if let Some(variadic) = variadic { - errors.push(syn::Error::new_spanned(variadic, "... not allowed here")); - } - if !matches!(output, ReturnType::Default) { - errors.push(syn::Error::new_spanned( - output, - "return type not allowed here", - )); - } - let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics })); let body_results = struct_generics.as_ref().and_then(|struct_generics| { errors.ok(transform_body::transform_body( module_kind, @@ -224,6 +280,47 @@ impl Parse for ModuleFn { errors.finish()?; let struct_generics = struct_generics.unwrap(); let (block, io) = body_results.unwrap(); + let config_options = match config_options { + HdlAttr { + pound_token, + style, + bracket_token, + kw: HdlOrHdlModule::Hdl((kw,)), + paren_token, + body, + } => { + debug_assert!(io.is_empty()); + return Ok(Self(ModuleFnImpl::Fn { + attrs, + config_options: HdlAttr { + pound_token, + style, + bracket_token, + kw, + paren_token, + body, + }, + vis, + sig, + block, + })); + } + HdlAttr { + pound_token, + style, + bracket_token, + kw: HdlOrHdlModule::HdlModule((kw,)), + paren_token, + body, + } => HdlAttr { + pound_token, + style, + bracket_token, + kw, + paren_token, + body, + }, + }; let (_struct_impl_generics, _struct_type_generics, struct_where_clause) = struct_generics.split_for_impl(); let struct_where_clause: Option = parse_quote! { #struct_where_clause }; @@ -259,22 +356,22 @@ impl Parse for ModuleFn { } }; let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?; - Ok(Self { + Ok(Self(ModuleFnImpl::Module(ModuleFnModule { attrs, config_options, - module_kind, + module_kind: module_kind.unwrap(), vis, sig, block, struct_generics, the_struct, - }) + }))) } } impl ModuleFn { pub(crate) fn generate(self) -> TokenStream { - let Self { + let ModuleFnModule { attrs, config_options, module_kind, @@ -283,7 +380,28 @@ impl ModuleFn { block, struct_generics, the_struct, - } = self; + } = match self.0 { + ModuleFnImpl::Module(v) => v, + ModuleFnImpl::Fn { + attrs, + config_options, + vis, + sig, + block, + } => { + let ConfigOptions { + outline_generated: _, + extern_: _, + } = config_options.body; + return ItemFn { + attrs, + vis, + sig, + block, + } + .into_token_stream(); + } + }; let ConfigOptions { outline_generated: _, extern_: _, diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index 76e1d69..d4d92b2 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -925,7 +925,7 @@ with_debug_clone_and_fold! { #[allow(dead_code)] pub(crate) struct HdlLet { pub(crate) attrs: Vec, - pub(crate) hdl_attr: HdlAttr, + pub(crate) hdl_attr: HdlAttr, pub(crate) let_token: Token![let], pub(crate) mut_token: Option, pub(crate) name: Ident, @@ -1112,7 +1112,7 @@ impl ToTokens for ImplicitName { } struct Visitor<'a> { - module_kind: ModuleKind, + module_kind: Option, errors: Errors, io: Vec, block_depth: usize, @@ -1120,22 +1120,33 @@ struct Visitor<'a> { } impl Visitor<'_> { - fn take_hdl_attr(&mut self, attrs: &mut Vec) -> Option> { + fn take_hdl_attr( + &mut self, + attrs: &mut Vec, + ) -> Option> { self.errors.unwrap_or( HdlAttr::parse_and_take_attr(attrs), Some(syn::parse2::(quote! {}).unwrap().into()), ) } - fn require_normal_module(&mut self, spanned: impl ToTokens) { + fn require_normal_module_or_fn(&mut self, spanned: impl ToTokens) { match self.module_kind { - ModuleKind::Extern => { + Some(ModuleKind::Extern) => { self.errors .error(spanned, "not allowed in #[hdl_module(extern)]"); } - ModuleKind::Normal => {} + Some(ModuleKind::Normal) | None => {} } } - fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { + fn require_module(&mut self, spanned: impl ToTokens) { + match self.module_kind { + None => { + self.errors.error(spanned, "not allowed in #[hdl] fn"); + } + Some(_) => {} + } + } + fn process_hdl_if(&mut self, hdl_attr: HdlAttr, expr_if: ExprIf) -> Expr { let ExprIf { attrs, if_token, @@ -1143,7 +1154,7 @@ impl Visitor<'_> { then_branch, else_branch, } = expr_if; - self.require_normal_module(if_token); + self.require_normal_module_or_fn(if_token); let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), expr => expr, @@ -1208,11 +1219,12 @@ impl Visitor<'_> { .to_tokens(expr); }); let mut attrs = hdl_let.attrs.clone(); + self.require_module(kind); match self.module_kind { - ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> + Some(ModuleKind::Extern) => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> #[allow(unused_variables)] }), - ModuleKind::Normal => {} + Some(ModuleKind::Normal) | None => {} } let let_stmt = Local { attrs, @@ -1249,7 +1261,7 @@ impl Visitor<'_> { }, semi_token, } = hdl_let; - self.require_normal_module(instance); + self.require_normal_module_or_fn(instance); let mut expr = instance.to_token_stream(); paren.surround(&mut expr, |expr| { let name_str = ImplicitName { @@ -1276,7 +1288,7 @@ impl Visitor<'_> { fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet) -> Local { let name = &hdl_let.name; let reg_builder = hdl_let.kind.reg_builder; - self.require_normal_module(reg_builder); + self.require_normal_module_or_fn(reg_builder); let mut expr = reg_builder.to_token_stream(); hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { let name_str = ImplicitName { @@ -1327,7 +1339,7 @@ impl Visitor<'_> { fn process_hdl_let_wire(&mut self, hdl_let: HdlLet) -> Local { let name = &hdl_let.name; let wire = hdl_let.kind.wire; - self.require_normal_module(wire); + self.require_normal_module_or_fn(wire); let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); let mut expr = wire.to_token_stream(); hdl_let.kind.paren.surround(&mut expr, |expr| { @@ -1361,7 +1373,7 @@ impl Visitor<'_> { let name = &hdl_let.name; let memory_fn = hdl_let.kind.memory_fn; let memory_fn_name = memory_fn.name(); - self.require_normal_module(memory_fn_name); + self.require_normal_module_or_fn(memory_fn_name); let mut expr = memory_fn_name.to_token_stream(); let (paren, arg) = match memory_fn { MemoryFn::Memory { @@ -1543,7 +1555,7 @@ impl Fold for Visitor<'_> { } fn fold_attribute(&mut self, attr: Attribute) -> Attribute { - if is_hdl_attr(&attr) { + if is_hdl_attr::(&attr) { self.errors .error(&attr, "#[hdl] attribute not supported here"); } @@ -1610,8 +1622,9 @@ impl Fold for Visitor<'_> { fn fold_local(&mut self, let_stmt: Local) -> Local { match self .errors - .ok(HdlAttr::::parse_and_leave_attr(&let_stmt.attrs)) - { + .ok(HdlAttr::::parse_and_leave_attr( + &let_stmt.attrs, + )) { None => return empty_let(), Some(None) => return fold_local(self, let_stmt), Some(Some(HdlAttr { .. })) => {} @@ -1646,7 +1659,7 @@ impl Fold for Visitor<'_> { } pub(crate) fn transform_body( - module_kind: ModuleKind, + module_kind: Option, mut body: Box, parsed_generics: &ParsedGenerics, ) -> syn::Result<(Box, Vec)> { diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs index 00ee706..b5a0ad3 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_aggregate_literals.rs @@ -1,6 +1,6 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information -use crate::{module::transform_body::Visitor, HdlAttr}; +use crate::{kw, module::transform_body::Visitor, HdlAttr}; use quote::{format_ident, quote_spanned}; use syn::{ parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, @@ -10,10 +10,10 @@ use syn::{ impl Visitor<'_> { pub(crate) fn process_hdl_array( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_array: ExprArray, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); for elem in &mut expr_array.elems { *elem = parse_quote_spanned! {elem.span()=> ::fayalite::expr::ToExpr::to_expr(&(#elem)) @@ -23,10 +23,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_repeat( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, mut expr_repeat: ExprRepeat, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); let repeated_value = &expr_repeat.expr; *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> ::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) @@ -35,10 +35,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_struct( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_struct: ExprStruct, ) -> Expr { - self.require_normal_module(&hdl_attr); + self.require_normal_module_or_fn(&hdl_attr); let name_span = expr_struct.path.segments.last().unwrap().ident.span(); let builder_ident = format_ident!("__builder", span = name_span); let empty_builder = if expr_struct.qself.is_some() @@ -91,10 +91,10 @@ impl Visitor<'_> { } pub(crate) fn process_hdl_tuple( &mut self, - hdl_attr: HdlAttr, + hdl_attr: HdlAttr, expr_tuple: ExprTuple, ) -> Expr { - self.require_normal_module(hdl_attr); + self.require_normal_module_or_fn(hdl_attr); parse_quote_spanned! {expr_tuple.span()=> ::fayalite::expr::ToExpr::to_expr(&#expr_tuple) } diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs index ae21a73..1d53104 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body/expand_match.rs @@ -2,6 +2,7 @@ // See Notices.txt for copyright information use crate::{ fold::{impl_fold, DoFold}, + kw, module::transform_body::{with_debug_clone_and_fold, Visitor}, Errors, HdlAttr, PairsIterExt, }; @@ -749,7 +750,7 @@ struct HdlMatchParseState<'a> { impl Visitor<'_> { pub(crate) fn process_hdl_match( &mut self, - _hdl_attr: HdlAttr, + _hdl_attr: HdlAttr, expr_match: ExprMatch, ) -> Expr { let span = expr_match.match_token.span(); @@ -761,7 +762,7 @@ impl Visitor<'_> { brace_token: _, arms, } = expr_match; - self.require_normal_module(match_token); + self.require_normal_module_or_fn(match_token); let mut state = HdlMatchParseState { match_span: span, errors: &mut self.errors, diff --git a/crates/fayalite/tests/ui/hdl_types.stderr b/crates/fayalite/tests/ui/hdl_types.stderr index e01b04f..a65d796 100644 --- a/crates/fayalite/tests/ui/hdl_types.stderr +++ b/crates/fayalite/tests/ui/hdl_types.stderr @@ -1,4 +1,4 @@ -error: top-level #[hdl] can only be used on structs or enums +error: top-level #[hdl] can only be used on structs, enums, or functions --> tests/ui/hdl_types.rs:5:1 | 5 | #[hdl]