diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs new file mode 100644 index 0000000..e5d5f7b --- /dev/null +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: LGPL-3.0-or-later +// See Notices.txt for copyright information +use crate::{ + hdl_type_common::{ + get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, + TypesParser, + }, + kw, Errors, HdlAttr, +}; +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{parse_quote_spanned, Attribute, Generics, Ident, ItemType, Token, Type, Visibility}; + +#[derive(Clone, Debug)] +pub(crate) struct ParsedTypeAlias { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) vis: Visibility, + pub(crate) type_token: Token![type], + pub(crate) ident: Ident, + pub(crate) generics: MaybeParsed, + pub(crate) eq_token: Token![=], + pub(crate) ty: MaybeParsed, + pub(crate) semi_token: Token![;], +} + +impl ParsedTypeAlias { + fn parse(item: ItemType) -> syn::Result { + let ItemType { + mut attrs, + vis, + type_token, + ident, + mut generics, + eq_token, + ty, + semi_token, + } = item; + let mut errors = Errors::new(); + let mut options = errors + .unwrap_or_default(HdlAttr::::parse_and_take_attr( + &mut attrs, + )) + .unwrap_or_default(); + errors.ok(options.body.validate()); + let ItemOptions { + outline_generated: _, + target: _, + custom_bounds, + no_static, + no_runtime_generics: _, + } = options.body; + if let Some((no_static,)) = no_static { + errors.error(no_static, "no_static is not valid on type aliases"); + } + let generics = if custom_bounds.is_some() { + MaybeParsed::Unrecognized(generics) + } else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) { + MaybeParsed::Parsed(generics) + } else { + MaybeParsed::Unrecognized(generics) + }; + let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors); + errors.finish()?; + Ok(Self { + attrs, + options, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + }) + } +} + +impl ToTokens for ParsedTypeAlias { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + vis, + type_token, + ident, + generics, + eq_token, + ty, + semi_token, + } = self; + let ItemOptions { + outline_generated: _, + target, + custom_bounds: _, + no_static: _, + no_runtime_generics, + } = &options.body; + let target = get_target(target, ident); + let mut type_attrs = attrs.clone(); + type_attrs.push(parse_quote_spanned! {ident.span()=> + #[allow(type_alias_bounds)] + }); + ItemType { + attrs: type_attrs, + vis: vis.clone(), + type_token: *type_token, + ident: ident.clone(), + generics: generics.into(), + eq_token: *eq_token, + ty: Box::new(ty.clone().into()), + semi_token: *semi_token, + } + .to_tokens(tokens); + if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) = + (generics, ty, no_runtime_generics) + { + generics.make_runtime_generics(tokens, vis, ident, &target, |context| { + ty.make_hdl_type_expr(context) + }) + } + } +} + +pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result { + let item = ParsedTypeAlias::parse(item)?; + let outline_generated = item.options.body.outline_generated; + let mut contents = item.to_token_stream(); + if outline_generated.is_some() { + contents = crate::outline_generated(contents, "hdl-type-alias-"); + } + Ok(contents) +} diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 194dbf3..0ffd4d4 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -16,6 +16,7 @@ use syn::{ mod fold; mod hdl_bundle; mod hdl_enum; +mod hdl_type_alias; mod hdl_type_common; mod module; @@ -850,6 +851,7 @@ macro_rules! options { }; } +use crate::hdl_type_alias::hdl_type_alias_impl; pub(crate) use options; pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream { @@ -906,14 +908,16 @@ pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result syn::Result { let kw = kw::hdl::default(); - let item = syn::parse2::(quote! { #[#kw(#attr)] #item })?; + let item = quote! { #[#kw(#attr)] #item }; + let item = syn::parse2::(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), + Item::Type(item) => hdl_type_alias_impl(item), _ => Err(syn::Error::new( Span::call_site(), - "top-level #[hdl] can only be used on structs, enums, or functions", + "top-level #[hdl] can only be used on structs, enums, type aliases, or functions", )), } } diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 4509146..b191016 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -31,6 +31,8 @@ pub enum E { A, B(UInt<3>), C(T), + D(TyAlias2), + E(TyAlias, { 1 + 2 }>), } #[hdl(outline_generated)] @@ -38,6 +40,12 @@ pub struct S2 { pub v: E, } +#[hdl(outline_generated)] +pub type TyAlias = Array, C>; + +#[hdl(outline_generated)] +pub type TyAlias2 = TyAlias, ConstUsize<24>, 5>; + // check that #[hdl] properly handles hygiene macro_rules! types_in_macros { ($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => { diff --git a/crates/fayalite/tests/ui/hdl_types.stderr b/crates/fayalite/tests/ui/hdl_types.stderr index a65d796..d3a3617 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, enums, or functions +error: top-level #[hdl] can only be used on structs, enums, type aliases, or functions --> tests/ui/hdl_types.rs:5:1 | 5 | #[hdl]