diff --git a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs index 8235366..0fa2222 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_alias.rs @@ -3,111 +3,19 @@ use crate::{ Errors, HdlAttr, hdl_type_common::{ - ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, TypesParser, - WrappedInConst, common_derives, get_target, known_items, + ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType, + PhantomConstGetBound, TypesParser, WrappedInConst, common_derives, get_target, known_items, }, kw, }; use proc_macro2::TokenStream; use quote::{ToTokens, format_ident, quote_spanned}; use syn::{ - AngleBracketedGenericArguments, Attribute, Expr, Fields, GenericArgument, GenericParam, - Generics, Ident, ItemStruct, ItemType, Path, PathArguments, Token, TraitBound, - TraitBoundModifier, Type, TypeGroup, TypeParam, TypeParamBound, TypeParen, Visibility, - parse_quote_spanned, punctuated::Pair, token::Paren, + Attribute, Expr, Fields, GenericParam, Generics, Ident, ItemStruct, ItemType, Token, Type, + TypeGroup, TypeParam, TypeParen, Visibility, parse_quote_spanned, punctuated::Pair, + token::Paren, }; -#[derive(Clone, Debug)] -pub(crate) struct PhantomConstGetBound { - pub(crate) phantom_const_get: known_items::PhantomConstGet, - pub(crate) colon2_token: Option, - pub(crate) lt_token: Token![<], - pub(crate) ty: Type, - pub(crate) comma_token: Option, - pub(crate) gt_token: Token![>], -} - -impl From for Path { - fn from(value: PhantomConstGetBound) -> Self { - let PhantomConstGetBound { - phantom_const_get, - colon2_token, - lt_token, - ty, - comma_token, - gt_token, - } = value; - let mut path = phantom_const_get.path; - path.segments.last_mut().expect("known to exist").arguments = - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - colon2_token, - lt_token, - args: FromIterator::from_iter([Pair::new(GenericArgument::Type(ty), comma_token)]), - gt_token, - }); - path - } -} - -impl From for TraitBound { - fn from(value: PhantomConstGetBound) -> Self { - let path = Path::from(value); - TraitBound { - paren_token: None, - modifier: TraitBoundModifier::None, - lifetimes: None, - path, - } - } -} - -impl From for TypeParamBound { - fn from(value: PhantomConstGetBound) -> Self { - TraitBound::from(value).into() - } -} - -impl PhantomConstGetBound { - fn parse_opt(bound: TypeParamBound) -> Option { - let TypeParamBound::Trait(TraitBound { - paren_token: None, - modifier: TraitBoundModifier::None, - lifetimes: None, - path, - }) = bound - else { - return None; - }; - let Ok(( - phantom_const_get, - PathArguments::AngleBracketed(AngleBracketedGenericArguments { - colon2_token, - lt_token, - args, - gt_token, - }), - )) = known_items::PhantomConstGet::parse_path_with_arguments(path) - else { - return None; - }; - let mut args = args.into_pairs(); - let (GenericArgument::Type(ty), comma_token) = args.next()?.into_tuple() else { - return None; - }; - let None = args.next() else { - return None; - }; - Some(Self { - phantom_const_get, - colon2_token, - lt_token, - ty, - comma_token, - gt_token, - }) - } -} - #[derive(Clone, Debug)] pub(crate) struct PhantomConstAccessorTypeParam { attrs: Vec, @@ -162,7 +70,9 @@ impl PhantomConstAccessorTypeParam { let colon_token = colon_token.unwrap_or(Token![:](ident.span())); let mut bounds = bounds.into_pairs(); let (bound, plus_token) = bounds.next()?.into_tuple(); - let phantom_const_get_bound = PhantomConstGetBound::parse_opt(bound)?; + let phantom_const_get_bound = PhantomConstGetBound::parse_type_param_bound(bound) + .ok()? + .ok()?; let None = bounds.next() else { return None; }; 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 73acc39..f5b353e 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -8,9 +8,9 @@ use syn::{ AngleBracketedGenericArguments, Attribute, Block, ConstParam, Expr, ExprBlock, ExprGroup, ExprIndex, ExprParen, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct, - Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, Turbofish, Type, - TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, TypeTuple, Visibility, WhereClause, - WherePredicate, + Path, PathArguments, PathSegment, PredicateType, QSelf, Stmt, Token, TraitBound, Turbofish, + Type, TypeGenerics, TypeGroup, TypeParam, TypeParamBound, TypeParen, TypePath, TypeTuple, + Visibility, WhereClause, WherePredicate, parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, punctuated::{Pair, Punctuated}, @@ -2065,6 +2065,174 @@ pub(crate) mod known_items { ); } +#[derive(Clone, Debug)] +pub(crate) struct PhantomConstGetBound { + pub(crate) phantom_const_get: known_items::PhantomConstGet, + pub(crate) colon2_token: Option, + pub(crate) lt_token: Token![<], + pub(crate) ty: Type, + pub(crate) comma_token: Option, + pub(crate) gt_token: Token![>], +} + +impl PhantomConstGetBound { + pub(crate) fn parse_path_with_arguments(path: Path) -> syn::Result> { + match known_items::PhantomConstGet::parse_path_with_arguments(path) { + Ok((phantom_const_get, arguments)) => { + Self::parse_path_and_arguments(phantom_const_get, arguments).map(Ok) + } + Err(path) => Ok(Err(path)), + } + } + pub(crate) fn parse_path_and_arguments( + phantom_const_get: known_items::PhantomConstGet, + arguments: PathArguments, + ) -> syn::Result { + let error = |arguments: PathArguments, message: &str| { + let mut path = phantom_const_get.path.clone(); + path.segments.last_mut().expect("known to exist").arguments = arguments; + syn::Error::new_spanned(path, message) + }; + match arguments { + PathArguments::None => Err(error(arguments, "missing generics for PhantomConstGet")), + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args, + gt_token, + }) => { + let error = |args: Punctuated, message| { + error( + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args, + gt_token, + }), + message, + ) + }; + let mut args = args.into_pairs().peekable(); + let Some((generic_argument, comma_token)) = args.next().map(Pair::into_tuple) + else { + return Err(error( + Default::default(), + "PhantomConstGet takes a type argument but no generic arguments were supplied", + )); + }; + if args.peek().is_some() { + return Err(error( + [Pair::new(generic_argument, comma_token)] + .into_iter() + .chain(args) + .collect(), + "PhantomConstGet takes a single type argument but too many generic arguments were supplied", + )); + }; + let GenericArgument::Type(ty) = generic_argument else { + return Err(error( + Punctuated::from_iter([Pair::new(generic_argument, comma_token)]), + "PhantomConstGet requires a type argument", + )); + }; + Ok(Self { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + }) + } + PathArguments::Parenthesized(_) => Err(error( + arguments, + "parenthetical generics are not valid for PhantomConstGet", + )), + } + } + pub(crate) fn parse_type_param_bound( + bound: TypeParamBound, + ) -> syn::Result> { + let TypeParamBound::Trait(TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + }) = bound + else { + return Ok(Err(bound)); + }; + Ok(match Self::parse_path_with_arguments(path)? { + Ok(v) => Ok(v), + Err(path) => Err(TypeParamBound::Trait(TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + })), + }) + } +} + +impl ToTokens for PhantomConstGetBound { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + } = self; + phantom_const_get.to_tokens(tokens); + colon2_token.to_tokens(tokens); + lt_token.to_tokens(tokens); + ty.to_tokens(tokens); + comma_token.to_tokens(tokens); + gt_token.to_tokens(tokens); + } +} + +impl From for Path { + fn from(value: PhantomConstGetBound) -> Self { + let PhantomConstGetBound { + phantom_const_get, + colon2_token, + lt_token, + ty, + comma_token, + gt_token, + } = value; + let mut path = phantom_const_get.path; + path.segments.last_mut().expect("known to exist").arguments = + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token, + lt_token, + args: FromIterator::from_iter([Pair::new(GenericArgument::Type(ty), comma_token)]), + gt_token, + }); + path + } +} + +impl From for TraitBound { + fn from(value: PhantomConstGetBound) -> Self { + let path = Path::from(value); + TraitBound { + paren_token: None, + modifier: syn::TraitBoundModifier::None, + lifetimes: None, + path, + } + } +} + +impl From for TypeParamBound { + fn from(value: PhantomConstGetBound) -> Self { + TraitBound::from(value).into() + } +} + macro_rules! impl_bounds { ( #[struct = $struct_type:ident] @@ -2072,6 +2240,10 @@ macro_rules! impl_bounds { $( $Variant:ident, )* + $( + #[has_body] + $VariantHasBody:ident($variant_has_body_ty:ty), + )* $( #[unknown] $Unknown:ident, @@ -2081,6 +2253,7 @@ macro_rules! impl_bounds { #[derive(Clone, Debug)] $vis enum $enum_type { $($Variant(known_items::$Variant),)* + $($VariantHasBody($variant_has_body_ty),)* $($Unknown(syn::TypeParamBound),)? } @@ -2090,31 +2263,42 @@ macro_rules! impl_bounds { } })* + $(impl From<$variant_has_body_ty> for $enum_type { + fn from(v: $variant_has_body_ty) -> Self { + Self::$VariantHasBody(v) + } + })* + impl ToTokens for $enum_type { fn to_tokens(&self, tokens: &mut TokenStream) { match self { $(Self::$Variant(v) => v.to_tokens(tokens),)* + $(Self::$VariantHasBody(v) => v.to_tokens(tokens),)* $(Self::$Unknown(v) => v.to_tokens(tokens),)? } } } impl $enum_type { - $vis fn parse_path(path: Path) -> Result { + $vis fn parse_path_with_arguments(path: Path) -> syn::Result> { #![allow(unreachable_code)] $(let path = match known_items::$Variant::parse_path(path) { - Ok(v) => return Ok(Self::$Variant(v)), + Ok(v) => return Ok(Ok(Self::$Variant(v))), Err(path) => path, };)* - $(return Ok(Self::$Unknown(syn::TraitBound { + $(let path = match <$variant_has_body_ty>::parse_path_with_arguments(path)? { + Ok(v) => return Ok(Ok(Self::$VariantHasBody(v))), + Err(path) => path, + };)* + $(return Ok(Ok(Self::$Unknown(syn::TraitBound { paren_token: None, modifier: syn::TraitBoundModifier::None, lifetimes: None, path, - }.into()));)? - Err(path) + }.into())));)? + Ok(Err(path)) } - $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> Result { + $vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result> { #![allow(unreachable_code)] if let syn::TypeParamBound::Trait(mut trait_bound) = type_param_bound { if let syn::TraitBound { @@ -2123,24 +2307,24 @@ macro_rules! impl_bounds { lifetimes: None, path: _, } = trait_bound { - match Self::parse_path(trait_bound.path) { - Ok(retval) => return Ok(retval), + match Self::parse_path_with_arguments(trait_bound.path)? { + Ok(retval) => return Ok(Ok(retval)), Err(path) => trait_bound.path = path, } } type_param_bound = trait_bound.into(); } - $(return Ok(Self::$Unknown(type_param_bound));)? - Err(type_param_bound) + $(return Ok(Ok(Self::$Unknown(type_param_bound)));)? + Ok(Err(type_param_bound)) } } impl Parse for $enum_type { fn parse(input: ParseStream) -> syn::Result { - Self::parse_type_param_bound(input.parse()?) + Self::parse_type_param_bound(input.parse()?)? .map_err(|type_param_bound| syn::Error::new_spanned( type_param_bound, - format_args!("expected one of: {}", [$(stringify!($Variant)),*].join(", ")), + format_args!("expected one of: {}", [$(stringify!($Variant),)* $(stringify!($VariantHasBody)),*].join(", ")), )) } } @@ -2149,6 +2333,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct $struct_type { $($vis $Variant: Option,)* + $($vis $VariantHasBody: Option<$variant_has_body_ty>,)* $($vis $Unknown: Vec,)? } @@ -2161,6 +2346,11 @@ macro_rules! impl_bounds { separator = Some(::default()); v.to_tokens(tokens); })* + $(if let Some(v) = &self.$VariantHasBody { + separator.to_tokens(tokens); + separator = Some(::default()); + v.to_tokens(tokens); + })* $(for v in &self.$Unknown { separator.to_tokens(tokens); separator = Some(::default()); @@ -2174,6 +2364,7 @@ macro_rules! impl_bounds { #[allow(non_snake_case)] $vis struct Iter { $($Variant: Option,)* + $($VariantHasBody: Option<$variant_has_body_ty>,)* $($Unknown: std::vec::IntoIter,)? } @@ -2184,6 +2375,7 @@ macro_rules! impl_bounds { fn into_iter(self) -> Self::IntoIter { Iter { $($Variant: self.$Variant,)* + $($VariantHasBody: self.$VariantHasBody,)* $($Unknown: self.$Unknown.into_iter(),)? } } @@ -2198,6 +2390,11 @@ macro_rules! impl_bounds { return Some($enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$VariantHasBody.take() { + return Some($enum_type::$VariantHasBody(value)); + } + )* $( if let Some(value) = self.$Unknown.next() { return Some($enum_type::$Unknown(value)); @@ -2213,6 +2410,11 @@ macro_rules! impl_bounds { init = f(init, $enum_type::$Variant(value)); } )* + $( + if let Some(value) = self.$VariantHasBody.take() { + init = f(init, $enum_type::$VariantHasBody(value)); + } + )* $( if let Some(value) = self.$Unknown.next() { init = f(init, $enum_type::$Unknown(value)); @@ -2229,6 +2431,9 @@ macro_rules! impl_bounds { $($enum_type::$Variant(v) => { self.$Variant = Some(v); })* + $($enum_type::$VariantHasBody(v) => { + self.$VariantHasBody = Some(v); + })* $($enum_type::$Unknown(v) => { self.$Unknown.push(v); })? @@ -2250,6 +2455,9 @@ macro_rules! impl_bounds { $(if let Some(v) = v.$Variant { self.$Variant = Some(v); })* + $(if let Some(v) = v.$VariantHasBody { + self.$VariantHasBody = Some(v); + })* $(self.$Unknown.extend(v.$Unknown);)* }); } @@ -2304,6 +2512,8 @@ impl_bounds! { Size, StaticType, Type, + #[has_body] + PhantomConstGet(PhantomConstGetBound), #[unknown] Unknown, } @@ -2319,6 +2529,8 @@ impl_bounds! { ResetType, StaticType, Type, + #[has_body] + PhantomConstGet(PhantomConstGetBound), #[unknown] Unknown, } @@ -2334,6 +2546,7 @@ impl From for ParsedBound { ParsedTypeBound::ResetType(v) => ParsedBound::ResetType(v), ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), ParsedTypeBound::Type(v) => ParsedBound::Type(v), + ParsedTypeBound::PhantomConstGet(v) => ParsedBound::PhantomConstGet(v), ParsedTypeBound::Unknown(v) => ParsedBound::Unknown(v), } } @@ -2349,6 +2562,7 @@ impl From for ParsedBounds { ResetType, StaticType, Type, + PhantomConstGet, Unknown, } = value; Self { @@ -2361,6 +2575,7 @@ impl From for ParsedBounds { Size: None, StaticType, Type, + PhantomConstGet, Unknown, } } @@ -2397,6 +2612,10 @@ impl ParsedTypeBound { ParsedTypeBound::Type(known_items::Type(span)), ]), Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + Self::PhantomConstGet(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), Self::Unknown(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::Unknown(v)]), } } @@ -2432,6 +2651,7 @@ impl From for ParsedBounds { Size, StaticType: None, Type: None, + PhantomConstGet: None, Unknown: vec![], } } @@ -2534,6 +2754,9 @@ impl ParsedBound { Self::Size(v) => ParsedBoundCategory::SizeType(ParsedSizeTypeBound::Size(v)), Self::StaticType(v) => ParsedBoundCategory::Type(ParsedTypeBound::StaticType(v)), Self::Type(v) => ParsedBoundCategory::Type(ParsedTypeBound::Type(v)), + Self::PhantomConstGet(v) => { + ParsedBoundCategory::Type(ParsedTypeBound::PhantomConstGet(v)) + } Self::Unknown(v) => ParsedBoundCategory::Unknown(v), } } @@ -3419,7 +3642,8 @@ impl ParsedGenerics { | ParsedTypeBound::BundleType(_) | ParsedTypeBound::EnumType(_) | ParsedTypeBound::IntType(_) - | ParsedTypeBound::ResetType(_) => { + | ParsedTypeBound::ResetType(_) + | ParsedTypeBound::PhantomConstGet(_) => { errors.error(bound, "bounds on mask types are not implemented"); } ParsedTypeBound::StaticType(bound) => { diff --git a/crates/fayalite/src/lib.rs b/crates/fayalite/src/lib.rs index 98849a6..96ee1f7 100644 --- a/crates/fayalite/src/lib.rs +++ b/crates/fayalite/src/lib.rs @@ -166,7 +166,7 @@ pub use fayalite_proc_macros::hdl_module; /// // you can then use it in other types: /// /// #[hdl(no_static)] -/// pub struct WrapMyArray> { +/// pub struct WrapMyArray> { /// pub my_array: GetMyArray

, /// } /// @@ -202,7 +202,7 @@ pub use fayalite_proc_macros::hdl_module; /// // you can then use it in other types: /// /// #[hdl(no_static)] -/// pub struct FlagPerItem> { +/// pub struct FlagPerItem> { /// pub flags: ArrayType>, /// } /// diff --git a/crates/fayalite/tests/hdl_types.rs b/crates/fayalite/tests/hdl_types.rs index 8742bb0..148cb64 100644 --- a/crates/fayalite/tests/hdl_types.rs +++ b/crates/fayalite/tests/hdl_types.rs @@ -210,7 +210,7 @@ pub type GetA> = DynSize; pub type GetB> = UInt; #[hdl(outline_generated, no_static)] -pub struct MyTypeWithPhantomConstParameter> { +pub struct MyTypeWithPhantomConstParameter> { pub a: ArrayType>, pub b: HdlOption>, }