add PhantomConstGet to the known Type bounds for #[hdl] struct/enum

This commit is contained in:
Jacob Lifshay 2025-10-27 20:08:22 -07:00
parent 4b24a88641
commit 0b82178740
Signed by: programmerjake
SSH key fingerprint: SHA256:HnFTLGpSm4Q4Fj502oCFisjZSoakwEuTsJJMSke63RQ
4 changed files with 251 additions and 117 deletions

View file

@ -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<Token![::]>,
pub(crate) lt_token: Token![<],
pub(crate) ty: Type,
pub(crate) comma_token: Option<Token![,]>,
pub(crate) gt_token: Token![>],
}
impl From<PhantomConstGetBound> 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<PhantomConstGetBound> for TraitBound {
fn from(value: PhantomConstGetBound) -> Self {
let path = Path::from(value);
TraitBound {
paren_token: None,
modifier: TraitBoundModifier::None,
lifetimes: None,
path,
}
}
}
impl From<PhantomConstGetBound> for TypeParamBound {
fn from(value: PhantomConstGetBound) -> Self {
TraitBound::from(value).into()
}
}
impl PhantomConstGetBound {
fn parse_opt(bound: TypeParamBound) -> Option<Self> {
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<Attribute>,
@ -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;
};

View file

@ -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<Token![::]>,
pub(crate) lt_token: Token![<],
pub(crate) ty: Type,
pub(crate) comma_token: Option<Token![,]>,
pub(crate) gt_token: Token![>],
}
impl PhantomConstGetBound {
pub(crate) fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
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<Self> {
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<GenericArgument, Token![,]>, 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<Result<Self, TypeParamBound>> {
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<PhantomConstGetBound> 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<PhantomConstGetBound> 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<PhantomConstGetBound> 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<Self, Path> {
$vis fn parse_path_with_arguments(path: Path) -> syn::Result<Result<Self, Path>> {
#![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<Self, syn::TypeParamBound> {
$vis fn parse_type_param_bound(mut type_param_bound: syn::TypeParamBound) -> syn::Result<Result<Self, syn::TypeParamBound>> {
#![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> {
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<known_items::$Variant>,)*
$($vis $VariantHasBody: Option<$variant_has_body_ty>,)*
$($vis $Unknown: Vec<syn::TypeParamBound>,)?
}
@ -2161,6 +2346,11 @@ macro_rules! impl_bounds {
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
$(if let Some(v) = &self.$VariantHasBody {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
v.to_tokens(tokens);
})*
$(for v in &self.$Unknown {
separator.to_tokens(tokens);
separator = Some(<Token![+]>::default());
@ -2174,6 +2364,7 @@ macro_rules! impl_bounds {
#[allow(non_snake_case)]
$vis struct Iter {
$($Variant: Option<known_items::$Variant>,)*
$($VariantHasBody: Option<$variant_has_body_ty>,)*
$($Unknown: std::vec::IntoIter<syn::TypeParamBound>,)?
}
@ -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<ParsedTypeBound> 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<ParsedTypeBounds> for ParsedBounds {
ResetType,
StaticType,
Type,
PhantomConstGet,
Unknown,
} = value;
Self {
@ -2361,6 +2575,7 @@ impl From<ParsedTypeBounds> 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<ParsedSizeTypeBounds> 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) => {

View file

@ -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<P: Type + PhantomConstGet<Config>> {
/// pub struct WrapMyArray<P: PhantomConstGet<Config>> {
/// pub my_array: GetMyArray<P>,
/// }
///
@ -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<P: Type + PhantomConstGet<Config>> {
/// pub struct FlagPerItem<P: PhantomConstGet<Config>> {
/// pub flags: ArrayType<Bool, GetItemsLen<P>>,
/// }
///

View file

@ -210,7 +210,7 @@ pub type GetA<P: PhantomConstGet<MyPhantomConstInner>> = DynSize;
pub type GetB<P: PhantomConstGet<MyPhantomConstInner>> = UInt;
#[hdl(outline_generated, no_static)]
pub struct MyTypeWithPhantomConstParameter<P: Type + PhantomConstGet<MyPhantomConstInner>> {
pub struct MyTypeWithPhantomConstParameter<P: PhantomConstGet<MyPhantomConstInner>> {
pub a: ArrayType<Bool, GetA<P>>,
pub b: HdlOption<GetB<P>>,
}