From 76ea7f82c3d9a2977e8bfe5d199abdf3927afaca Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 16 Sep 2024 16:47:10 -0700 Subject: [PATCH 1/2] WIP adding const generics --- .../src/hdl_type_common.rs | 569 ++++++++++++++---- 1 file changed, 445 insertions(+), 124 deletions(-) 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); + } } } } From 2c1afd1cd63f8da01300bf866c510f5db313da2b Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Tue, 17 Sep 2024 15:39:23 -0700 Subject: [PATCH 2/2] const generics on hdl_module work! --- .../src/hdl_type_common.rs | 359 +++++++++++++++--- .../fayalite-proc-macros-impl/src/module.rs | 65 ++-- crates/fayalite/tests/module.rs | 17 +- 3 files changed, 355 insertions(+), 86 deletions(-) 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 0ae6480..bc0b074 100644 --- a/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs +++ b/crates/fayalite-proc-macros-impl/src/hdl_type_common.rs @@ -11,8 +11,8 @@ use syn::{ AngleBracketedGenericArguments, Attribute, ConstParam, Expr, ExprIndex, ExprPath, ExprTuple, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Generics, Ident, ImplGenerics, Index, ItemStruct, Path, PathArguments, PathSegment, - PredicateType, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, - TypeTuple, Visibility, WhereClause, WherePredicate, + PredicateType, QSelf, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParen, + TypePath, TypeTuple, Visibility, WhereClause, WherePredicate, }; crate::options! { @@ -1236,7 +1236,6 @@ impl ParseTypes for ParsedType { parser .errors .error(ident, "constant provided when a type was expected"); - todo!(); return Err(ParseFailed); } }, @@ -1524,6 +1523,7 @@ pub(crate) enum UnparsedGenericParam { ident: Ident, colon_token: Token![:], bounds: ParsedBounds, + mask_type_bounds: ParsedTypeBounds, }, Const { attrs: Vec, @@ -1532,6 +1532,7 @@ pub(crate) enum UnparsedGenericParam { ident: Ident, colon_token: Token![:], ty: ParsedConstGenericType, + bounds: Option, }, } @@ -1595,6 +1596,20 @@ pub(crate) mod known_items { Err(path) } } + #[allow(dead_code)] + pub(crate) fn parse_path_with_arguments(mut path: Path) -> Result<(Self, PathArguments), Path> { + let Some(last_segment) = path.segments.last_mut() else { + return Err(path); + }; + let arguments = std::mem::replace(&mut last_segment.arguments, PathArguments::None); + match Self::parse_path(path) { + Ok(retval) => Ok((retval, arguments)), + Err(mut path) => { + path.segments.last_mut().unwrap().arguments = arguments; + Err(path) + } + } + } } impl Parse for $known_item { @@ -1664,6 +1679,7 @@ pub(crate) mod known_items { impl_known_item!(::fayalite::ty::CanonicalType); impl_known_item!(::fayalite::ty::StaticType); impl_known_item!(::fayalite::ty::Type); + impl_known_item!(::fayalite::ty::Type::MaskType); impl_known_item!(::fayalite::util::ConstUsize); impl_known_item!(::fayalite::__std::primitive::usize); } @@ -1822,6 +1838,19 @@ macro_rules! impl_bounds { Ok(retval) } } + + impl $struct_type { + #[allow(dead_code)] + $vis fn add_implied_bounds(&mut self) { + let orig_bounds = self.clone(); + self.extend( + self.clone() + .into_iter() + .map($enum_type::implied_bounds), + ); + self.extend([orig_bounds]); // keep spans of explicitly provided bounds + } + } }; } @@ -1849,6 +1878,64 @@ impl_bounds! { } } +impl From for ParsedBound { + fn from(value: ParsedTypeBound) -> Self { + match value { + ParsedTypeBound::BundleType(v) => ParsedBound::BundleType(v), + ParsedTypeBound::EnumType(v) => ParsedBound::EnumType(v), + ParsedTypeBound::IntType(v) => ParsedBound::IntType(v), + ParsedTypeBound::StaticType(v) => ParsedBound::StaticType(v), + ParsedTypeBound::Type(v) => ParsedBound::Type(v), + } + } +} + +impl From for ParsedBounds { + fn from(value: ParsedTypeBounds) -> Self { + let ParsedTypeBounds { + BundleType, + EnumType, + IntType, + StaticType, + Type, + } = value; + Self { + BundleType, + EnumType, + IntType, + KnownSize: None, + Size: None, + StaticType, + Type, + } + } +} + +impl ParsedTypeBound { + fn implied_bounds(self) -> ParsedTypeBounds { + let span = self.span(); + match self { + Self::BundleType(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), + Self::EnumType(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), + Self::IntType(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), + Self::StaticType(v) => ParsedTypeBounds::from_iter([ + ParsedTypeBound::from(v), + ParsedTypeBound::Type(known_items::Type(span)), + ]), + Self::Type(v) => ParsedTypeBounds::from_iter([ParsedTypeBound::from(v)]), + } + } +} + impl_bounds! { #[struct = ParsedSizeTypeBounds] pub(crate) enum ParsedSizeTypeBound { @@ -1857,6 +1944,43 @@ impl_bounds! { } } +impl From for ParsedBound { + fn from(value: ParsedSizeTypeBound) -> Self { + match value { + ParsedSizeTypeBound::KnownSize(v) => ParsedBound::KnownSize(v), + ParsedSizeTypeBound::Size(v) => ParsedBound::Size(v), + } + } +} + +impl From for ParsedBounds { + fn from(value: ParsedSizeTypeBounds) -> Self { + let ParsedSizeTypeBounds { KnownSize, Size } = value; + Self { + BundleType: None, + EnumType: None, + IntType: None, + KnownSize, + Size, + StaticType: None, + Type: None, + } + } +} + +impl ParsedSizeTypeBound { + fn implied_bounds(self) -> ParsedSizeTypeBounds { + let span = self.span(); + match self { + Self::KnownSize(v) => ParsedSizeTypeBounds::from_iter([ + ParsedSizeTypeBound::from(v), + ParsedSizeTypeBound::Size(known_items::Size(span)), + ]), + Self::Size(v) => ParsedSizeTypeBounds::from_iter([ParsedSizeTypeBound::from(v)]), + } + } +} + #[derive(Clone, Debug)] pub(crate) enum ParsedBoundsCategory { Type(ParsedTypeBounds), @@ -1918,42 +2042,13 @@ impl ParsedBound { } } fn implied_bounds(self) -> ParsedBounds { - let span = self.span(); - match self { - Self::BundleType(v) => ParsedBounds::from_iter([ - ParsedBound::from(v), - ParsedBound::Type(known_items::Type(span)), - ]), - Self::EnumType(v) => ParsedBounds::from_iter([ - ParsedBound::from(v), - ParsedBound::Type(known_items::Type(span)), - ]), - Self::IntType(v) => ParsedBounds::from_iter([ - ParsedBound::from(v), - ParsedBound::Type(known_items::Type(span)), - ]), - Self::KnownSize(v) => ParsedBounds::from_iter([ - ParsedBound::from(v), - ParsedBound::Size(known_items::Size(span)), - ]), - Self::Size(v) => ParsedBounds::from_iter([ParsedBound::from(v)]), - Self::StaticType(v) => ParsedBounds::from_iter([ - ParsedBound::from(v), - ParsedBound::Type(known_items::Type(span)), - ]), - Self::Type(v) => ParsedBounds::from_iter([ParsedBound::from(v)]), + match self.categorize() { + ParsedBoundCategory::Type(v) => v.implied_bounds().into(), + ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(), } } } -impl ParsedBounds { - fn add_implied_bounds(&mut self) { - let orig_bounds = self.clone(); - self.extend(self.clone().into_iter().map(ParsedBound::implied_bounds)); - self.extend([orig_bounds]); // keep spans of explicitly provided bounds - } -} - #[derive(Debug, Clone)] pub(crate) struct ParsedTypeParam { pub(crate) attrs: Vec, @@ -2116,6 +2211,7 @@ impl ParsedGenerics { } ParsedGenericParam::Const(ParsedConstParam { ident, bounds, .. }) => { bounds + .bounds .KnownSize .get_or_insert_with(|| known_items::KnownSize(ident.span())); } @@ -2527,6 +2623,7 @@ impl ParsedGenerics { ident: ident.clone(), colon_token, bounds: ParsedBounds::default(), + mask_type_bounds: ParsedTypeBounds::default(), }, LateParsedParam { default: default @@ -2563,6 +2660,7 @@ impl ParsedGenerics { ident: ident.clone(), colon_token: *colon_token, ty: ParsedConstGenericType::Usize(known_items::usize(ident.span())), + bounds: None, }, LateParsedParam { default: None, @@ -2591,20 +2689,139 @@ impl ParsedGenerics { lifetimes: None, bounded_ty: Type::Path(TypePath { - qself: None, + qself, path: bounded_ty, }), - colon_token: _, + colon_token, bounds: unparsed_bounds, }) = predicate else { errors.error(predicate, "unsupported where predicate kind"); continue; }; - ParsedTypeNamed { - path: todo!(), - args: todo!(), - }; + if let Some(qself) = &qself { + if let QSelf { + lt_token: _, + ty: base_ty, + position: 3, + as_token: Some(_), + gt_token: _, + } = qself + { + if bounded_ty.segments.len() == 4 && unparsed_bounds.len() == 1 { + if let ( + Ok(_), + Type::Path(TypePath { + qself: None, + path: base_ty, + }), + ) = ( + known_items::MaskType::parse_path(bounded_ty.clone()), + &**base_ty, + ) { + let Some(&index) = base_ty + .get_ident() + .and_then(|base_ty| param_name_to_index_map.get(base_ty)) + else { + errors.error( + TypePath { + qself: Some(qself.clone()), + path: bounded_ty, + }, + "unsupported where predicate kind", + ); + continue; + }; + let parsed_bounds = match &mut unparsed_params[index] { + UnparsedGenericParam::Type { + mask_type_bounds, .. + } => mask_type_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(), + ))); + continue; + } + } + } + errors.error( + TypePath { + qself: Some(qself.clone()), + path: bounded_ty, + }, + "unsupported where predicate kind", + ); + continue; + } + if let Ok(( + const_usize, + PathArguments::AngleBracketed(AngleBracketedGenericArguments { + colon2_token: _, + lt_token, + args, + gt_token, + }), + )) = known_items::ConstUsize::parse_path_with_arguments(bounded_ty.clone()) + { + if args.len() != 1 { + errors.error(const_usize, "ConstUsize must have one argument"); + continue; + } + let GenericArgument::Type(Type::Path(TypePath { + qself: None, + path: arg, + })) = &args[0] + else { + errors.error( + const_usize, + "the only supported ConstUsize argument is a const generic parameter", + ); + continue; + }; + let arg = arg.get_ident(); + let Some((arg, &index)) = + arg.and_then(|arg| Some((arg, param_name_to_index_map.get(arg)?))) + else { + errors.error( + const_usize, + "the only supported ConstUsize argument is a const generic parameter", + ); + continue; + }; + let parsed_bounds = match &mut unparsed_params[index] { + UnparsedGenericParam::Const { bounds, .. } => bounds, + UnparsedGenericParam::Type { ident, .. } => { + errors.error( + bounded_ty, + format_args!("expected const generic parameter, found type `{ident}`"), + ); + continue; + } + }; + parsed_bounds + .get_or_insert_with(|| ParsedConstParamWhereBounds { + const_usize, + lt_token, + ident: arg.clone(), + gt_token, + colon_token, + bounds: ParsedSizeTypeBounds::default(), + }) + .bounds + .extend(errors.ok(syn::parse2::( + unparsed_bounds.to_token_stream(), + ))); + continue; + } let Some(&index) = bounded_ty .get_ident() .and_then(|bounded_ty| param_name_to_index_map.get(bounded_ty)) @@ -2639,7 +2856,26 @@ impl ParsedGenerics { ident, colon_token, mut bounds, + mask_type_bounds, } => { + for bound in mask_type_bounds { + bounds + .Type + .get_or_insert_with(|| known_items::Type(bound.span())); + match bound { + ParsedTypeBound::BundleType(_) + | ParsedTypeBound::EnumType(_) + | ParsedTypeBound::IntType(_) => { + errors.error(bound, "bound on mask type not implemented"); + } + ParsedTypeBound::StaticType(bound) => { + if bounds.StaticType.is_none() { + errors.error(bound, "StaticType bound on mask type without corresponding StaticType bound on original type is not implemented"); + } + }, + ParsedTypeBound::Type(_) => {} + } + } bounds.add_implied_bounds(); match bounds.categorize(&mut errors, ident.span()) { ParsedBoundsCategory::Type(bounds) => { @@ -2671,18 +2907,35 @@ impl ParsedGenerics { 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, - }), + bounds, + } => { + let span = ident.span(); + let mut bounds = bounds.unwrap_or_else(|| ParsedConstParamWhereBounds { + const_usize: known_items::ConstUsize(span), + lt_token: Token![<](span), + ident: ident.clone(), + gt_token: Token![>](span), + colon_token: Token![:](span), + bounds: ParsedSizeTypeBounds { + KnownSize: None, + Size: Some(known_items::Size(span)), + }, + }); + bounds + .bounds + .Size + .get_or_insert_with(|| known_items::Size(span)); + bounds.bounds.add_implied_bounds(); + ParsedGenericParam::Const(ParsedConstParam { + bounds, + attrs, + options, + const_token, + ident, + colon_token, + ty, + }) + } }, )); let mut retval = Self { diff --git a/crates/fayalite-proc-macros-impl/src/module.rs b/crates/fayalite-proc-macros-impl/src/module.rs index 7d816d3..0945abb 100644 --- a/crates/fayalite-proc-macros-impl/src/module.rs +++ b/crates/fayalite-proc-macros-impl/src/module.rs @@ -66,8 +66,8 @@ pub(crate) struct ModuleFn { vis: Visibility, sig: Signature, block: Box, - io: Vec, struct_generics: ParsedGenerics, + the_struct: TokenStream, } #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] @@ -224,6 +224,41 @@ impl Parse for ModuleFn { errors.finish()?; let struct_generics = struct_generics.unwrap(); let (block, io) = body_results.unwrap(); + 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 }; + if let Some(struct_where_clause) = &struct_where_clause { + sig.generics + .where_clause + .get_or_insert_with(|| WhereClause { + where_token: struct_where_clause.where_token, + predicates: Default::default(), + }) + .predicates + .extend(struct_where_clause.predicates.clone()); + } + let fn_name = &sig.ident; + let io_flips = io + .iter() + .map(|io| match io.kind.kind { + ModuleIOKind::Input((input,)) => quote_spanned! {input.span=> + #[hdl(flip)] + }, + ModuleIOKind::Output(_) => quote! {}, + }) + .collect::>(); + let io_types = io.iter().map(|io| &io.kind.ty).collect::>(); + let io_names = io.iter().map(|io| &io.name).collect::>(); + let the_struct: ItemStruct = parse_quote! { + #[allow(non_camel_case_types)] + #[hdl(no_runtime_generics, no_static)] + #vis struct #fn_name #struct_generics #struct_where_clause { + #( + #io_flips + #vis #io_names: #io_types,)* + } + }; + let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?; Ok(Self { attrs, config_options, @@ -231,8 +266,8 @@ impl Parse for ModuleFn { vis, sig, block, - io, struct_generics, + the_struct, }) } } @@ -246,8 +281,8 @@ impl ModuleFn { vis, sig, block, - io, struct_generics, + the_struct, } = self; let ConfigOptions { outline_generated: _, @@ -279,7 +314,7 @@ impl ModuleFn { ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal }, }; let fn_name = &outer_sig.ident; - let (_struct_impl_generics, struct_type_generics, struct_where_clause) = + let (_struct_impl_generics, struct_type_generics, _struct_where_clause) = struct_generics.split_for_impl(); let struct_ty = quote! {#fn_name #struct_type_generics}; body_sig.ident = parse_quote! {__body}; @@ -294,17 +329,6 @@ impl ModuleFn { }; outer_sig.output = parse_quote! {-> ::fayalite::intern::Interned<::fayalite::module::Module<#struct_ty>>}; - let io_flips = io - .iter() - .map(|io| match io.kind.kind { - ModuleIOKind::Input((input,)) => quote_spanned! {input.span=> - #[hdl(flip)] - }, - ModuleIOKind::Output(_) => quote! {}, - }) - .collect::>(); - let io_types = io.iter().map(|io| &io.kind.ty).collect::>(); - let io_names = io.iter().map(|io| &io.name).collect::>(); let fn_name_str = fn_name.to_string(); let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl(); let body_turbofish_type_generics = body_type_generics.as_turbofish(); @@ -316,15 +340,6 @@ impl ModuleFn { |m| __body #body_turbofish_type_generics(m, #(#param_names,)*), ) }}; - let the_struct: ItemStruct = parse_quote! { - #[allow(non_camel_case_types)] - #[hdl(no_runtime_generics, no_static)] - #vis struct #fn_name #struct_generics #struct_where_clause { - #( - #io_flips - #vis #io_names: #io_types,)* - } - }; let outer_fn = ItemFn { attrs, vis, @@ -332,7 +347,7 @@ impl ModuleFn { block, }; let mut retval = outer_fn.into_token_stream(); - retval.extend(crate::hdl_bundle::hdl_bundle(the_struct).unwrap()); + retval.extend(the_struct); retval } } diff --git a/crates/fayalite/tests/module.rs b/crates/fayalite/tests/module.rs index 8df3771..f039750 100644 --- a/crates/fayalite/tests/module.rs +++ b/crates/fayalite/tests/module.rs @@ -6,6 +6,7 @@ use fayalite::{ intern::Intern, module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind}, prelude::*, + ty::StaticType, }; use serde_json::json; @@ -133,9 +134,11 @@ circuit my_module: }; } -#[cfg(todo)] #[hdl_module(outline_generated)] -pub fn check_array_repeat() { +pub fn check_array_repeat() +where + ConstUsize: KnownSize, +{ #[hdl] let i: UInt<8> = m.input(); #[hdl] @@ -147,7 +150,6 @@ pub fn check_array_repeat() { ); } -#[cfg(todo)] #[test] fn test_array_repeat() { let _n = SourceLocation::normalize_files_for_tests(); @@ -188,21 +190,21 @@ circuit check_array_repeat_1: }; } -#[cfg(todo)] #[hdl_module(outline_generated)] pub fn check_skipped_generics(v: U) where - T: StaticValue, + T: StaticType, + ConstUsize: KnownSize, U: std::fmt::Display, { dbg!(M); #[hdl] let i: T = m.input(); #[hdl] - let o: Array<[T; N]> = m.output(); + let o: Array = m.output(); let bytes = v.to_string().as_bytes().to_expr(); #[hdl] - let o2: Array<[UInt<8>]> = m.output(bytes.ty()); + let o2: Array> = m.output(Expr::ty(bytes)); connect( o, #[hdl] @@ -211,7 +213,6 @@ where connect(o2, bytes); } -#[cfg(todo)] #[test] fn test_skipped_generics() { let _n = SourceLocation::normalize_files_for_tests();