Compare commits

..

2 commits

Author SHA1 Message Date
Jacob Lifshay 2c1afd1cd6
const generics on hdl_module work!
All checks were successful
/ test (push) Successful in 4m54s
2024-09-17 15:39:23 -07:00
Jacob Lifshay 76ea7f82c3
WIP adding const generics
Some checks failed
/ test (push) Failing after 1m3s
2024-09-16 16:47:10 -07:00
3 changed files with 782 additions and 192 deletions

View file

@ -5,14 +5,14 @@ use std::{collections::HashMap, fmt, mem};
use syn::{ use syn::{
parse::{Parse, ParseStream}, parse::{Parse, ParseStream},
parse_quote, parse_quote_spanned, parse_quote, parse_quote_spanned,
punctuated::Punctuated, punctuated::{Pair, Punctuated},
spanned::Spanned, spanned::Spanned,
token::{Brace, Bracket, Paren}, token::{Brace, Bracket, Paren},
AngleBracketedGenericArguments, Attribute, ConstParam, Expr, ExprIndex, ExprPath, ExprTuple, AngleBracketedGenericArguments, Attribute, ConstParam, Expr, ExprIndex, ExprPath, ExprTuple,
Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam,
Generics, Ident, ImplGenerics, Index, ItemStruct, Path, PathArguments, PathSegment, Generics, Ident, ImplGenerics, Index, ItemStruct, Path, PathArguments, PathSegment,
PredicateType, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParen, TypePath, PredicateType, QSelf, Token, Turbofish, Type, TypeGenerics, TypeGroup, TypeParam, TypeParen,
TypeTuple, Visibility, WhereClause, WherePredicate, TypePath, TypeTuple, Visibility, WhereClause, WherePredicate,
}; };
crate::options! { crate::options! {
@ -239,7 +239,34 @@ impl ParseTypes<GenericArgument> for ParsedGenericArgument {
parser: &mut TypesParser<'_>, parser: &mut TypesParser<'_>,
) -> Result<Self, ParseFailed> { ) -> Result<Self, ParseFailed> {
match arg { 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(&param_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())), GenericArgument::Const(expr) => Ok(Self::Const(expr.clone())),
_ => parse_failed!(parser, arg, "expected type or const generic argument"), _ => parse_failed!(parser, arg, "expected type or const generic argument"),
} }
@ -1205,6 +1232,12 @@ impl ParseTypes<Path> for ParsedType {
param_index, param_index,
}) })
} }
ParsedGenericParam::Const(ParsedConstParam { ref ident, .. }) => {
parser
.errors
.error(ident, "constant provided when a type was expected");
return Err(ParseFailed);
}
}, },
)); ));
} }
@ -1291,6 +1324,120 @@ impl ParseTypes<Type> for ParsedType {
} }
} }
#[derive(Debug, Clone)]
pub(crate) enum ParsedConstGenericType {
Usize(known_items::usize),
}
impl_fold! {
enum ParsedConstGenericType<> {
Usize(known_items::usize),
}
}
impl From<ParsedConstGenericType> for Type {
fn from(value: ParsedConstGenericType) -> Self {
match value {
ParsedConstGenericType::Usize(v) => parse_quote! { #v },
}
}
}
impl From<MaybeParsed<ParsedConstGenericType, Type>> for Type {
fn from(value: MaybeParsed<ParsedConstGenericType, Type>) -> Self {
match value {
MaybeParsed::Unrecognized(value) => value,
MaybeParsed::Parsed(value) => value.into(),
}
}
}
impl From<Box<ParsedConstGenericType>> for Type {
fn from(value: Box<ParsedConstGenericType>) -> 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<Path> for ParsedConstGenericType {
fn parse_types(path: &mut Path, parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
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<TypePath> for ParsedConstGenericType {
fn parse_types(ty: &mut TypePath, parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
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<Type> for ParsedConstGenericType {
fn parse_types(ty: &mut Type, parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(match ty {
Type::Path(ty) => parser.parse(ty)?,
_ => parse_failed!(parser, ty, "unsupported const generic type"),
})
}
}
pub(crate) struct ParseFailed; pub(crate) struct ParseFailed;
impl fmt::Display for ParseFailed { impl fmt::Display for ParseFailed {
@ -1376,6 +1523,16 @@ pub(crate) enum UnparsedGenericParam {
ident: Ident, ident: Ident,
colon_token: Token![:], colon_token: Token![:],
bounds: ParsedBounds, bounds: ParsedBounds,
mask_type_bounds: ParsedTypeBounds,
},
Const {
attrs: Vec<Attribute>,
options: HdlAttr<ConstParamOptions>,
const_token: Token![const],
ident: Ident,
colon_token: Token![:],
ty: ParsedConstGenericType,
bounds: Option<ParsedConstParamWhereBounds>,
}, },
} }
@ -1390,7 +1547,7 @@ pub(crate) mod known_items {
macro_rules! impl_known_item_body { macro_rules! impl_known_item_body {
($known_item:ident) => { ($known_item:ident) => {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[allow(dead_code)] #[allow(dead_code, non_camel_case_types)]
pub(crate) struct $known_item { pub(crate) struct $known_item {
pub(crate) path: Path, pub(crate) path: Path,
pub(crate) span: Span, pub(crate) span: Span,
@ -1439,6 +1596,20 @@ pub(crate) mod known_items {
Err(path) 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 { impl Parse for $known_item {
@ -1508,7 +1679,9 @@ pub(crate) mod known_items {
impl_known_item!(::fayalite::ty::CanonicalType); impl_known_item!(::fayalite::ty::CanonicalType);
impl_known_item!(::fayalite::ty::StaticType); impl_known_item!(::fayalite::ty::StaticType);
impl_known_item!(::fayalite::ty::Type); impl_known_item!(::fayalite::ty::Type);
impl_known_item!(::fayalite::ty::Type::MaskType);
impl_known_item!(::fayalite::util::ConstUsize); impl_known_item!(::fayalite::util::ConstUsize);
impl_known_item!(::fayalite::__std::primitive::usize);
} }
macro_rules! impl_bounds { macro_rules! impl_bounds {
@ -1665,6 +1838,19 @@ macro_rules! impl_bounds {
Ok(retval) 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
}
}
}; };
} }
@ -1692,6 +1878,64 @@ impl_bounds! {
} }
} }
impl From<ParsedTypeBound> 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<ParsedTypeBounds> 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! { impl_bounds! {
#[struct = ParsedSizeTypeBounds] #[struct = ParsedSizeTypeBounds]
pub(crate) enum ParsedSizeTypeBound { pub(crate) enum ParsedSizeTypeBound {
@ -1700,6 +1944,43 @@ impl_bounds! {
} }
} }
impl From<ParsedSizeTypeBound> for ParsedBound {
fn from(value: ParsedSizeTypeBound) -> Self {
match value {
ParsedSizeTypeBound::KnownSize(v) => ParsedBound::KnownSize(v),
ParsedSizeTypeBound::Size(v) => ParsedBound::Size(v),
}
}
}
impl From<ParsedSizeTypeBounds> 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)] #[derive(Clone, Debug)]
pub(crate) enum ParsedBoundsCategory { pub(crate) enum ParsedBoundsCategory {
Type(ParsedTypeBounds), Type(ParsedTypeBounds),
@ -1761,42 +2042,13 @@ impl ParsedBound {
} }
} }
fn implied_bounds(self) -> ParsedBounds { fn implied_bounds(self) -> ParsedBounds {
let span = self.span(); match self.categorize() {
match self { ParsedBoundCategory::Type(v) => v.implied_bounds().into(),
Self::BundleType(v) => ParsedBounds::from_iter([ ParsedBoundCategory::SizeType(v) => v.implied_bounds().into(),
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)]),
} }
} }
} }
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)] #[derive(Debug, Clone)]
pub(crate) struct ParsedTypeParam { pub(crate) struct ParsedTypeParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
@ -1865,10 +2117,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<Attribute>,
pub(crate) options: HdlAttr<ConstParamOptions>,
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)] #[derive(Debug, Clone)]
pub(crate) enum ParsedGenericParam { pub(crate) enum ParsedGenericParam {
Type(ParsedTypeParam), Type(ParsedTypeParam),
SizeType(ParsedSizeTypeParam), SizeType(ParsedSizeTypeParam),
Const(ParsedConstParam),
} }
impl ToTokens for ParsedGenericParam { impl ToTokens for ParsedGenericParam {
@ -1876,6 +2172,7 @@ impl ToTokens for ParsedGenericParam {
match self { match self {
ParsedGenericParam::Type(v) => v.to_tokens(tokens), ParsedGenericParam::Type(v) => v.to_tokens(tokens),
ParsedGenericParam::SizeType(v) => v.to_tokens(tokens), ParsedGenericParam::SizeType(v) => v.to_tokens(tokens),
ParsedGenericParam::Const(v) => v.to_tokens(tokens),
} }
} }
} }
@ -1885,6 +2182,7 @@ impl ParsedGenericParam {
match self { match self {
ParsedGenericParam::Type(v) => &v.ident, ParsedGenericParam::Type(v) => &v.ident,
ParsedGenericParam::SizeType(v) => &v.ident, ParsedGenericParam::SizeType(v) => &v.ident,
ParsedGenericParam::Const(v) => &v.ident,
} }
} }
} }
@ -1911,6 +2209,12 @@ impl ParsedGenerics {
.KnownSize .KnownSize
.get_or_insert_with(|| known_items::KnownSize(ident.span())); .get_or_insert_with(|| known_items::KnownSize(ident.span()));
} }
ParsedGenericParam::Const(ParsedConstParam { ident, bounds, .. }) => {
bounds
.bounds
.KnownSize
.get_or_insert_with(|| known_items::KnownSize(ident.span()));
}
} }
} }
self self
@ -1982,6 +2286,13 @@ impl ParsedGenerics {
<#ident as ::fayalite::int::Size>::SizeType <#ident as ::fayalite::int::Size>::SizeType
} }
} }
ParsedGenericParam::Const(param) => {
let ident = &param.ident;
parse_quote_spanned! {span=>
<::fayalite::util::ConstUsize<#ident>
as ::fayalite::int::Size>::SizeType
}
}
}) })
.collect(), .collect(),
} }
@ -2254,28 +2565,35 @@ impl ParsedGenerics {
todo!(); todo!();
} }
} }
ParsedGenericParam::Const(_) => todo!(),
}; };
} }
} }
pub(crate) fn parse(generics: &mut Generics) -> syn::Result<Self> { pub(crate) fn parse<'a>(generics: &'a mut Generics) -> syn::Result<Self> {
let Generics { let Generics {
lt_token, lt_token,
params, params: input_params,
gt_token, gt_token,
where_clause, where_clause,
} = generics; } = generics;
let mut errors = Errors::new(); let mut errors = Errors::new();
let mut predicates: Vec<WherePredicate> = Vec::with_capacity(params.len()); let mut predicates: Vec<WherePredicate> = Vec::with_capacity(input_params.len());
let mut defaults = Vec::with_capacity(params.len()); struct LateParsedParam<'a> {
let mut params = Punctuated::<UnparsedGenericParam, _>::from_iter( default: Option<(Token![=], &'a mut Type)>,
params.pairs_mut().filter_map_pair_value_mut(|param| { const_param_type: Option<&'a mut Type>,
let (param, default) = match param { }
let mut late_parsed_params: Vec<LateParsedParam<'a>> =
Vec::with_capacity(input_params.len());
let mut unparsed_params: Punctuated<UnparsedGenericParam, _> = 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) => { GenericParam::Lifetime(param) => {
errors.unwrap_or_default( errors.unwrap_or_default(HdlAttr::<LifetimeParamOptions>::parse_and_take_attr(
HdlAttr::<LifetimeParamOptions>::parse_and_take_attr(&mut param.attrs), &mut param.attrs,
); ));
errors.error(param, "lifetime generics are not supported by #[hdl]"); errors.error(param, "lifetime generics are not supported by #[hdl]");
return None; continue;
} }
GenericParam::Type(TypeParam { GenericParam::Type(TypeParam {
attrs, attrs,
@ -2287,9 +2605,7 @@ impl ParsedGenerics {
}) => { }) => {
let span = ident.span(); let span = ident.span();
let options = errors let options = errors
.unwrap_or_default(HdlAttr::<TypeParamOptions>::parse_and_take_attr( .unwrap_or_default(HdlAttr::<TypeParamOptions>::parse_and_take_attr(attrs))
attrs,
))
.unwrap_or_default(); .unwrap_or_default();
let colon_token = colon_token.unwrap_or_else(|| Token![:](span)); let colon_token = colon_token.unwrap_or_else(|| Token![:](span));
if !bounds.is_empty() { if !bounds.is_empty() {
@ -2307,10 +2623,14 @@ impl ParsedGenerics {
ident: ident.clone(), ident: ident.clone(),
colon_token, colon_token,
bounds: ParsedBounds::default(), bounds: ParsedBounds::default(),
mask_type_bounds: ParsedTypeBounds::default(),
}, },
default LateParsedParam {
.clone() default: default
.as_mut()
.map(|v| (eq_token.unwrap_or_else(|| Token![=](span)), v)), .map(|v| (eq_token.unwrap_or_else(|| Token![=](span)), v)),
const_param_type: None,
},
) )
} }
GenericParam::Const(ConstParam { GenericParam::Const(ConstParam {
@ -2323,29 +2643,41 @@ impl ParsedGenerics {
default, default,
}) => { }) => {
let options = errors let options = errors
.unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr( .unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr(attrs))
attrs,
))
.unwrap_or_default(); .unwrap_or_default();
let _ = const_token; if let Some(default) = default {
let _ = ident;
let _ = colon_token;
let _ = ty;
let _ = eq_token; let _ = eq_token;
let _ = default; errors.error(
let _ = options; default,
todo!() "const generics' default values are not yet implemented",
);
}
(
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())),
bounds: None,
},
LateParsedParam {
default: None,
const_param_type: Some(ty),
},
)
} }
}; };
defaults.push(default); late_parsed_params.push(late_parsed_param);
Some(param) unparsed_params.extend([Pair::new(unparsed_param, punct.cloned())]);
}), }
); let param_name_to_index_map: HashMap<Ident, usize> = unparsed_params
let param_name_to_index_map: HashMap<Ident, usize> = params
.iter() .iter()
.enumerate() .enumerate()
.map(|(index, param)| { .map(|(index, param)| {
let UnparsedGenericParam::Type { ident, .. } = param; let (UnparsedGenericParam::Type { ident, .. }
| UnparsedGenericParam::Const { ident, .. }) = param;
(ident.clone(), index) (ident.clone(), index)
}) })
.collect(); .collect();
@ -2357,43 +2689,193 @@ impl ParsedGenerics {
lifetimes: None, lifetimes: None,
bounded_ty: bounded_ty:
Type::Path(TypePath { Type::Path(TypePath {
qself: None, qself,
path: bounded_ty, path: bounded_ty,
}), }),
colon_token: _, colon_token,
bounds: unparsed_bounds, bounds: unparsed_bounds,
}) = predicate }) = predicate
else { else {
errors.error(predicate, "unsupported where predicate kind"); errors.error(predicate, "unsupported where predicate kind");
continue; continue;
}; };
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::<ParsedTypeBounds>(
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::<ParsedSizeTypeBounds>(
unparsed_bounds.to_token_stream(),
)));
continue;
}
let Some(&index) = bounded_ty let Some(&index) = bounded_ty
.get_ident() .get_ident()
.and_then(|bounded_ty| param_name_to_index_map.get(bounded_ty)) .and_then(|bounded_ty| param_name_to_index_map.get(bounded_ty))
else { else {
errors.error( errors.error(
bounded_ty, 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<A_CONST_PARAM>`",
); );
continue; continue;
}; };
let UnparsedGenericParam::Type { let parsed_bounds = match &mut unparsed_params[index] {
bounds: parsed_bounds, UnparsedGenericParam::Type { bounds, .. } => bounds,
.. UnparsedGenericParam::Const { ident, .. } => {
} = &mut params[index]; errors.error(
bounded_ty,
format_args!("expected type, found const parameter `{ident}`"),
);
continue;
}
};
parsed_bounds.extend(errors.ok(syn::parse2::<ParsedBounds>( parsed_bounds.extend(errors.ok(syn::parse2::<ParsedBounds>(
unparsed_bounds.to_token_stream(), unparsed_bounds.to_token_stream(),
))); )));
} }
let params = let params =
Punctuated::from_iter(params.into_pairs().map_pair_value(|param| match param { Punctuated::from_iter(unparsed_params.into_pairs().map_pair_value(
|param| match param {
UnparsedGenericParam::Type { UnparsedGenericParam::Type {
attrs, attrs,
options, options,
ident, ident,
colon_token, colon_token,
mut bounds, 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(); bounds.add_implied_bounds();
match bounds.categorize(&mut errors, ident.span()) { match bounds.categorize(&mut errors, ident.span()) {
ParsedBoundsCategory::Type(bounds) => { ParsedBoundsCategory::Type(bounds) => {
@ -2418,29 +2900,85 @@ impl ParsedGenerics {
} }
} }
} }
})); UnparsedGenericParam::Const {
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 { let mut retval = Self {
lt_token: *lt_token, lt_token: *lt_token,
params, params,
gt_token: *gt_token, gt_token: *gt_token,
param_name_to_index_map, param_name_to_index_map,
}; };
for (cur_param_index, default) in defaults.into_iter().enumerate() { for (
let Some((eq, mut ty)) = default else { cur_param_index,
continue; LateParsedParam {
}; default,
let Ok(ty) = TypesParser { const_param_type,
},
) in late_parsed_params.into_iter().enumerate()
{
let mut parser = TypesParser {
generics: &retval, generics: &retval,
cur_param_index: Some(cur_param_index), cur_param_index: Some(cur_param_index),
errors: &mut errors, 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] { match &mut retval.params[cur_param_index] {
ParsedGenericParam::Type(ParsedTypeParam { default, .. }) ParsedGenericParam::Type(ParsedTypeParam { default, .. })
| ParsedGenericParam::SizeType(ParsedSizeTypeParam { 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 +3055,21 @@ impl ToTokens for ParsedGenericsImplGenerics<'_> {
colon_token.to_tokens(tokens); colon_token.to_tokens(tokens);
bounds.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); punct.to_tokens(tokens);
} }
@ -2609,6 +3162,27 @@ impl ToTokens for ParsedGenericsWhereClause<'_> {
} }
} }
ParsedGenericParam::SizeType(_) => {} 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);
}
} }
} }
} }

View file

@ -66,8 +66,8 @@ pub(crate) struct ModuleFn {
vis: Visibility, vis: Visibility,
sig: Signature, sig: Signature,
block: Box<Block>, block: Box<Block>,
io: Vec<ModuleIO>,
struct_generics: ParsedGenerics, struct_generics: ParsedGenerics,
the_struct: TokenStream,
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
@ -224,6 +224,41 @@ impl Parse for ModuleFn {
errors.finish()?; errors.finish()?;
let struct_generics = struct_generics.unwrap(); let struct_generics = struct_generics.unwrap();
let (block, io) = body_results.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<WhereClause> = 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::<Vec<_>>();
let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
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 { Ok(Self {
attrs, attrs,
config_options, config_options,
@ -231,8 +266,8 @@ impl Parse for ModuleFn {
vis, vis,
sig, sig,
block, block,
io,
struct_generics, struct_generics,
the_struct,
}) })
} }
} }
@ -246,8 +281,8 @@ impl ModuleFn {
vis, vis,
sig, sig,
block, block,
io,
struct_generics, struct_generics,
the_struct,
} = self; } = self;
let ConfigOptions { let ConfigOptions {
outline_generated: _, outline_generated: _,
@ -279,7 +314,7 @@ impl ModuleFn {
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal }, ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
}; };
let fn_name = &outer_sig.ident; 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(); struct_generics.split_for_impl();
let struct_ty = quote! {#fn_name #struct_type_generics}; let struct_ty = quote! {#fn_name #struct_type_generics};
body_sig.ident = parse_quote! {__body}; body_sig.ident = parse_quote! {__body};
@ -294,17 +329,6 @@ impl ModuleFn {
}; };
outer_sig.output = outer_sig.output =
parse_quote! {-> ::fayalite::intern::Interned<::fayalite::module::Module<#struct_ty>>}; 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::<Vec<_>>();
let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
let fn_name_str = fn_name.to_string(); let fn_name_str = fn_name.to_string();
let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl(); let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl();
let body_turbofish_type_generics = body_type_generics.as_turbofish(); 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,)*), |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 { let outer_fn = ItemFn {
attrs, attrs,
vis, vis,
@ -332,7 +347,7 @@ impl ModuleFn {
block, block,
}; };
let mut retval = outer_fn.into_token_stream(); let mut retval = outer_fn.into_token_stream();
retval.extend(crate::hdl_bundle::hdl_bundle(the_struct).unwrap()); retval.extend(the_struct);
retval retval
} }
} }

View file

@ -6,6 +6,7 @@ use fayalite::{
intern::Intern, intern::Intern,
module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind}, module::transform::simplify_enums::{simplify_enums, SimplifyEnumsKind},
prelude::*, prelude::*,
ty::StaticType,
}; };
use serde_json::json; use serde_json::json;
@ -133,9 +134,11 @@ circuit my_module:
}; };
} }
#[cfg(todo)]
#[hdl_module(outline_generated)] #[hdl_module(outline_generated)]
pub fn check_array_repeat<const N: usize>() { pub fn check_array_repeat<const N: usize>()
where
ConstUsize<N>: KnownSize,
{
#[hdl] #[hdl]
let i: UInt<8> = m.input(); let i: UInt<8> = m.input();
#[hdl] #[hdl]
@ -147,7 +150,6 @@ pub fn check_array_repeat<const N: usize>() {
); );
} }
#[cfg(todo)]
#[test] #[test]
fn test_array_repeat() { fn test_array_repeat() {
let _n = SourceLocation::normalize_files_for_tests(); let _n = SourceLocation::normalize_files_for_tests();
@ -188,21 +190,21 @@ circuit check_array_repeat_1:
}; };
} }
#[cfg(todo)]
#[hdl_module(outline_generated)] #[hdl_module(outline_generated)]
pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U) pub fn check_skipped_generics<T, #[hdl(skip)] U, const N: usize, #[hdl(skip)] const M: usize>(v: U)
where where
T: StaticValue, T: StaticType,
ConstUsize<N>: KnownSize,
U: std::fmt::Display, U: std::fmt::Display,
{ {
dbg!(M); dbg!(M);
#[hdl] #[hdl]
let i: T = m.input(); let i: T = m.input();
#[hdl] #[hdl]
let o: Array<[T; N]> = m.output(); let o: Array<T, N> = m.output();
let bytes = v.to_string().as_bytes().to_expr(); let bytes = v.to_string().as_bytes().to_expr();
#[hdl] #[hdl]
let o2: Array<[UInt<8>]> = m.output(bytes.ty()); let o2: Array<UInt<8>> = m.output(Expr::ty(bytes));
connect( connect(
o, o,
#[hdl] #[hdl]
@ -211,7 +213,6 @@ where
connect(o2, bytes); connect(o2, bytes);
} }
#[cfg(todo)]
#[test] #[test]
fn test_skipped_generics() { fn test_skipped_generics() {
let _n = SourceLocation::normalize_files_for_tests(); let _n = SourceLocation::normalize_files_for_tests();