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 57b1034..0ae6480 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -5,7 +5,7 @@ use std::{collections::HashMap, fmt, mem}; use syn::{ parse::{Parse, ParseStream}, parse_quote, parse_quote_spanned, - punctuated::Punctuated, + punctuated::{Pair, Punctuated}, spanned::Spanned, token::{Brace, Bracket, Paren}, AngleBracketedGenericArguments, Attribute, ConstParam, Expr, ExprIndex, ExprPath, ExprTuple, @@ -239,7 +239,34 @@ impl ParseTypes for ParsedGenericArgument { parser: &mut TypesParser<'_>, ) -> Result { match arg { - GenericArgument::Type(ty) => Ok(Self::Type(parser.parse(ty)?)), + GenericArgument::Type(ty) => { + { + let mut ty = &*ty; + while let Type::Group(TypeGroup { elem, .. }) = ty { + ty = &**elem; + } + if let Type::Path(TypePath { qself: None, path }) = ty { + if let Some(ident) = path.get_ident() { + if let Some(¶m_index) = + parser.generics.param_name_to_index_map.get(ident) + { + match parser.generics.params[param_index] { + ParsedGenericParam::Type(_) + | ParsedGenericParam::SizeType(_) => {} + ParsedGenericParam::Const(_) => { + return Ok(Self::Const(Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: path.clone(), + }))); + } + } + } + } + } + } + Ok(Self::Type(parser.parse(ty)?)) + } GenericArgument::Const(expr) => Ok(Self::Const(expr.clone())), _ => parse_failed!(parser, arg, "expected type or const generic argument"), } @@ -1205,6 +1232,13 @@ impl ParseTypes for ParsedType { param_index, }) } + ParsedGenericParam::Const(ParsedConstParam { ref ident, .. }) => { + parser + .errors + .error(ident, "constant provided when a type was expected"); + todo!(); + return Err(ParseFailed); + } }, )); } @@ -1291,6 +1325,120 @@ impl ParseTypes for ParsedType { } } +#[derive(Debug, Clone)] +pub(crate) enum ParsedConstGenericType { + Usize(known_items::usize), +} + +impl_fold! { + enum ParsedConstGenericType<> { + Usize(known_items::usize), + } +} + +impl From for Type { + fn from(value: ParsedConstGenericType) -> Self { + match value { + ParsedConstGenericType::Usize(v) => parse_quote! { #v }, + } + } +} + +impl From> for Type { + fn from(value: MaybeParsed) -> Self { + match value { + MaybeParsed::Unrecognized(value) => value, + MaybeParsed::Parsed(value) => value.into(), + } + } +} + +impl From> for Type { + fn from(value: Box) -> Self { + (*value).into() + } +} + +impl ToTokens for ParsedConstGenericType { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + ParsedConstGenericType::Usize(ty) => ty.to_tokens(tokens), + } + } +} + +impl ParseTypes for ParsedConstGenericType { + fn parse_types(path: &mut Path, parser: &mut TypesParser<'_>) -> Result { + let Path { + leading_colon, + ref mut segments, + } = *path; + if segments.is_empty() { + parse_failed!(parser, path, "path must not be empty"); + } + let mut args = None; + let segments = Punctuated::from_iter(segments.pairs_mut().map_pair_value_mut(|segment| { + let PathSegment { ident, arguments } = segment; + if let Some(_) = args { + parser + .errors() + .error(&ident, "associated types/consts are not yet implemented"); + } + args = match arguments { + PathArguments::None => None, + PathArguments::AngleBracketed(args) => parser.parse(args).ok(), + PathArguments::Parenthesized(_) => { + parser + .errors() + .error(&segment, "function traits are not allowed"); + None + } + }; + PathSegment::from(segment.ident.clone()) + })); + let named = ParsedTypeNamed { + path: Path { + leading_colon, + segments, + }, + args, + }; + let named = match known_items::usize::try_from_named(named, parser)? { + Ok(v) => return Ok(Self::Usize(v)), + Err(named) => named, + }; + parser.errors.error( + named, + "const parameter types other than `usize` are not yet implemented", + ); + Err(ParseFailed) + } +} + +impl ParseTypes for ParsedConstGenericType { + fn parse_types(ty: &mut TypePath, parser: &mut TypesParser<'_>) -> Result { + let TypePath { qself, path } = ty; + if let Some(_qself) = qself { + parse_failed!( + parser, + ty, + "associated types/consts are not yet implemented" + ); + } else { + parser.parse(path) + } + } +} + +impl ParseTypes for ParsedConstGenericType { + fn parse_types(ty: &mut Type, parser: &mut TypesParser<'_>) -> Result { + Ok(match ty { + Type::Path(ty) => parser.parse(ty)?, + _ => parse_failed!(parser, ty, "unsupported const generic type"), + }) + } +} + pub(crate) struct ParseFailed; impl fmt::Display for ParseFailed { @@ -1377,6 +1525,14 @@ pub(crate) enum UnparsedGenericParam { colon_token: Token![:], bounds: ParsedBounds, }, + Const { + attrs: Vec, + options: HdlAttr, + const_token: Token![const], + ident: Ident, + colon_token: Token![:], + ty: ParsedConstGenericType, + }, } pub(crate) mod known_items { @@ -1390,7 +1546,7 @@ pub(crate) mod known_items { macro_rules! impl_known_item_body { ($known_item:ident) => { #[derive(Clone, Debug)] - #[allow(dead_code)] + #[allow(dead_code, non_camel_case_types)] pub(crate) struct $known_item { pub(crate) path: Path, pub(crate) span: Span, @@ -1509,6 +1665,7 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::ty::StaticType); impl_known_item!(::fayalite::ty::Type); impl_known_item!(::fayalite::util::ConstUsize); + impl_known_item!(::fayalite::__std::primitive::usize); } macro_rules! impl_bounds { @@ -1865,10 +2022,54 @@ impl ToTokens for ParsedSizeTypeParam { } } +#[derive(Debug, Clone)] +pub(crate) struct ParsedConstParamWhereBounds { + pub(crate) const_usize: known_items::ConstUsize, + pub(crate) lt_token: Token![<], + pub(crate) ident: Ident, + pub(crate) gt_token: Token![>], + pub(crate) colon_token: Token![:], + pub(crate) bounds: ParsedSizeTypeBounds, +} + +#[derive(Debug, Clone)] +pub(crate) struct ParsedConstParam { + pub(crate) attrs: Vec, + pub(crate) options: HdlAttr, + pub(crate) const_token: Token![const], + pub(crate) ident: Ident, + pub(crate) colon_token: Token![:], + pub(crate) ty: ParsedConstGenericType, + pub(crate) bounds: ParsedConstParamWhereBounds, +} + +impl ToTokens for ParsedConstParam { + fn to_tokens(&self, tokens: &mut TokenStream) { + let Self { + attrs, + options, + const_token, + ident, + colon_token, + ty, + bounds: _, + } = self; + let ConstParamOptions {} = options.body; + for attr in attrs { + attr.to_tokens(tokens); + } + const_token.to_tokens(tokens); + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + ty.to_tokens(tokens); + } +} + #[derive(Debug, Clone)] pub(crate) enum ParsedGenericParam { Type(ParsedTypeParam), SizeType(ParsedSizeTypeParam), + Const(ParsedConstParam), } impl ToTokens for ParsedGenericParam { @@ -1876,6 +2077,7 @@ impl ToTokens for ParsedGenericParam { match self { ParsedGenericParam::Type(v) => v.to_tokens(tokens), ParsedGenericParam::SizeType(v) => v.to_tokens(tokens), + ParsedGenericParam::Const(v) => v.to_tokens(tokens), } } } @@ -1885,6 +2087,7 @@ impl ParsedGenericParam { match self { ParsedGenericParam::Type(v) => &v.ident, ParsedGenericParam::SizeType(v) => &v.ident, + ParsedGenericParam::Const(v) => &v.ident, } } } @@ -1911,6 +2114,11 @@ impl ParsedGenerics { .KnownSize .get_or_insert_with(|| known_items::KnownSize(ident.span())); } + ParsedGenericParam::Const(ParsedConstParam { ident, bounds, .. }) => { + bounds + .KnownSize + .get_or_insert_with(|| known_items::KnownSize(ident.span())); + } } } self @@ -1982,6 +2190,13 @@ impl ParsedGenerics { <#ident as ::fayalite::int::Size>::SizeType } } + ParsedGenericParam::Const(param) => { + let ident = ¶m.ident; + parse_quote_spanned! {span=> + <::fayalite::util::ConstUsize<#ident> + as ::fayalite::int::Size>::SizeType + } + } }) .collect(), } @@ -2254,98 +2469,117 @@ impl ParsedGenerics { todo!(); } } + ParsedGenericParam::Const(_) => todo!(), }; } } - pub(crate) fn parse(generics: &mut Generics) -> syn::Result { + pub(crate) fn parse<'a>(generics: &'a mut Generics) -> syn::Result { let Generics { lt_token, - params, + params: input_params, gt_token, where_clause, } = generics; let mut errors = Errors::new(); - let mut predicates: Vec = Vec::with_capacity(params.len()); - let mut defaults = Vec::with_capacity(params.len()); - let mut params = Punctuated::::from_iter( - params.pairs_mut().filter_map_pair_value_mut(|param| { - let (param, default) = match param { - GenericParam::Lifetime(param) => { - errors.unwrap_or_default( - HdlAttr::::parse_and_take_attr(&mut param.attrs), - ); - errors.error(param, "lifetime generics are not supported by #[hdl]"); - return None; + let mut predicates: Vec = Vec::with_capacity(input_params.len()); + struct LateParsedParam<'a> { + default: Option<(Token![=], &'a mut Type)>, + const_param_type: Option<&'a mut Type>, + } + let mut late_parsed_params: Vec> = + Vec::with_capacity(input_params.len()); + let mut unparsed_params: Punctuated = Punctuated::new(); + for input_param in input_params.pairs_mut() { + 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.error(param, "lifetime generics are not supported by #[hdl]"); + continue; + } + GenericParam::Type(TypeParam { + attrs, + ident, + colon_token, + bounds, + eq_token, + default, + }) => { + let span = ident.span(); + let options = errors + .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() { + predicates.push(WherePredicate::Type(PredicateType { + lifetimes: None, + bounded_ty: parse_quote! { #ident }, + colon_token, + bounds: bounds.clone(), + })); } - GenericParam::Type(TypeParam { - attrs, - ident, - colon_token, - bounds, - eq_token, - default, - }) => { - let span = ident.span(); - let options = errors - .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() { - predicates.push(WherePredicate::Type(PredicateType { - lifetimes: None, - bounded_ty: parse_quote! { #ident }, - colon_token, - bounds: bounds.clone(), - })); - } - ( - UnparsedGenericParam::Type { - attrs: attrs.clone(), - options, - ident: ident.clone(), - colon_token, - bounds: ParsedBounds::default(), - }, - default - .clone() + ( + UnparsedGenericParam::Type { + attrs: attrs.clone(), + options, + ident: ident.clone(), + colon_token, + bounds: ParsedBounds::default(), + }, + LateParsedParam { + default: default + .as_mut() .map(|v| (eq_token.unwrap_or_else(|| Token![=](span)), v)), - ) - } - GenericParam::Const(ConstParam { - attrs, - const_token, - ident, - colon_token, - ty, - eq_token, - default, - }) => { - let options = errors - .unwrap_or_default(HdlAttr::::parse_and_take_attr( - attrs, - )) - .unwrap_or_default(); - let _ = const_token; - let _ = ident; - let _ = colon_token; - let _ = ty; + const_param_type: None, + }, + ) + } + GenericParam::Const(ConstParam { + attrs, + const_token, + ident, + colon_token, + ty, + eq_token, + default, + }) => { + let options = errors + .unwrap_or_default(HdlAttr::::parse_and_take_attr(attrs)) + .unwrap_or_default(); + if let Some(default) = default { let _ = eq_token; - let _ = default; - let _ = options; - todo!() + errors.error( + default, + "const generics' default values are not yet implemented", + ); } - }; - defaults.push(default); - Some(param) - }), - ); - let param_name_to_index_map: HashMap = params + ( + UnparsedGenericParam::Const { + attrs: attrs.clone(), + options, + const_token: *const_token, + ident: ident.clone(), + colon_token: *colon_token, + ty: ParsedConstGenericType::Usize(known_items::usize(ident.span())), + }, + LateParsedParam { + default: None, + const_param_type: Some(ty), + }, + ) + } + }; + late_parsed_params.push(late_parsed_param); + unparsed_params.extend([Pair::new(unparsed_param, punct.cloned())]); + } + let param_name_to_index_map: HashMap = unparsed_params .iter() .enumerate() .map(|(index, param)| { - let UnparsedGenericParam::Type { ident, .. } = param; + let (UnparsedGenericParam::Type { ident, .. } + | UnparsedGenericParam::Const { ident, .. }) = param; (ident.clone(), index) }) .collect(); @@ -2367,80 +2601,131 @@ impl ParsedGenerics { errors.error(predicate, "unsupported where predicate kind"); continue; }; + ParsedTypeNamed { + path: todo!(), + args: todo!(), + }; let Some(&index) = bounded_ty .get_ident() .and_then(|bounded_ty| param_name_to_index_map.get(bounded_ty)) else { errors.error( bounded_ty, - "where predicate bounded type must be one of the generic parameters", + "where predicate bounded type must be one of the generic type \ + parameters or `ConstUsize`", ); continue; }; - let UnparsedGenericParam::Type { - bounds: parsed_bounds, - .. - } = &mut params[index]; + let parsed_bounds = match &mut unparsed_params[index] { + UnparsedGenericParam::Type { bounds, .. } => bounds, + UnparsedGenericParam::Const { ident, .. } => { + errors.error( + bounded_ty, + format_args!("expected type, found const parameter `{ident}`"), + ); + continue; + } + }; parsed_bounds.extend(errors.ok(syn::parse2::( unparsed_bounds.to_token_stream(), ))); } let params = - Punctuated::from_iter(params.into_pairs().map_pair_value(|param| match param { - UnparsedGenericParam::Type { - attrs, - options, - ident, - colon_token, - mut bounds, - } => { - bounds.add_implied_bounds(); - match bounds.categorize(&mut errors, ident.span()) { - ParsedBoundsCategory::Type(bounds) => { - ParsedGenericParam::Type(ParsedTypeParam { - attrs, - options, - ident, - colon_token, - bounds, - default: None, - }) - } - ParsedBoundsCategory::SizeType(bounds) => { - ParsedGenericParam::SizeType(ParsedSizeTypeParam { - attrs, - options, - ident, - colon_token, - bounds, - default: None, - }) + Punctuated::from_iter(unparsed_params.into_pairs().map_pair_value( + |param| match param { + UnparsedGenericParam::Type { + attrs, + options, + ident, + colon_token, + mut bounds, + } => { + bounds.add_implied_bounds(); + match bounds.categorize(&mut errors, ident.span()) { + ParsedBoundsCategory::Type(bounds) => { + ParsedGenericParam::Type(ParsedTypeParam { + attrs, + options, + ident, + colon_token, + bounds, + default: None, + }) + } + ParsedBoundsCategory::SizeType(bounds) => { + ParsedGenericParam::SizeType(ParsedSizeTypeParam { + attrs, + options, + ident, + colon_token, + bounds, + default: None, + }) + } } } - } - })); + UnparsedGenericParam::Const { + attrs, + options, + const_token, + ident, + colon_token, + ty, + } => ParsedGenericParam::Const(ParsedConstParam { + bounds: ParsedSizeTypeBounds { + KnownSize: None, + Size: Some(known_items::Size(ident.span())), + }, + attrs, + options, + const_token, + ident, + colon_token, + ty, + }), + }, + )); let mut retval = Self { lt_token: *lt_token, params, gt_token: *gt_token, param_name_to_index_map, }; - for (cur_param_index, default) in defaults.into_iter().enumerate() { - let Some((eq, mut ty)) = default else { - continue; - }; - let Ok(ty) = TypesParser { + for ( + cur_param_index, + LateParsedParam { + default, + const_param_type, + }, + ) in late_parsed_params.into_iter().enumerate() + { + let mut parser = TypesParser { generics: &retval, cur_param_index: Some(cur_param_index), errors: &mut errors, - } - .parse(&mut ty) else { - continue; }; + let parsed_default = default.and_then(|(eq, ty)| { + let ty = parser.parse(ty).ok()?; + Some((eq, ty)) + }); + let parsed_const_param_type = const_param_type.and_then(|ty| parser.parse(ty).ok()); match &mut retval.params[cur_param_index] { ParsedGenericParam::Type(ParsedTypeParam { default, .. }) | ParsedGenericParam::SizeType(ParsedSizeTypeParam { default, .. }) => { - *default = Some((eq, ty)) + *default = parsed_default; + } + ParsedGenericParam::Const(ParsedConstParam { + attrs: _, + options: _, + const_token: _, + ident: _, + colon_token: _, + ty, + bounds: _, + }) => { + if let Some(parsed_const_param_type) = parsed_const_param_type { + *ty = parsed_const_param_type; + } } } } @@ -2517,6 +2802,21 @@ impl ToTokens for ParsedGenericsImplGenerics<'_> { colon_token.to_tokens(tokens); bounds.to_tokens(tokens); } + ParsedGenericParam::Const(ParsedConstParam { + attrs: _, + options, + const_token, + ident, + colon_token, + ty, + bounds: _, + }) => { + let ConstParamOptions {} = options.body; + const_token.to_tokens(tokens); + ident.to_tokens(tokens); + colon_token.to_tokens(tokens); + ty.to_tokens(tokens); + } } punct.to_tokens(tokens); } @@ -2609,6 +2909,27 @@ impl ToTokens for ParsedGenericsWhereClause<'_> { } } ParsedGenericParam::SizeType(_) => {} + ParsedGenericParam::Const(ParsedConstParam { + ident: _, + bounds: + ParsedConstParamWhereBounds { + const_usize, + lt_token, + ident, + gt_token, + colon_token, + bounds, + }, + .. + }) => { + where_token(ident.span()).to_tokens(tokens); + const_usize.to_tokens(tokens); + lt_token.to_tokens(tokens); + ident.to_tokens(tokens); + gt_token.to_tokens(tokens); + colon_token.to_tokens(tokens); + bounds.to_tokens(tokens); + } } } }