Compare commits

...

4 commits

15 changed files with 916 additions and 167 deletions

View file

@ -19,13 +19,13 @@ use syn::{
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedBundle { pub(crate) struct ParsedBundle {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>, pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
pub(crate) vis: Visibility, pub(crate) vis: Visibility,
pub(crate) struct_token: Token![struct], pub(crate) struct_token: Token![struct],
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>, pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>, pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip>>>, pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
pub(crate) mask_type_ident: Ident, pub(crate) mask_type_ident: Ident,
pub(crate) mask_type_match_variant_ident: Ident, pub(crate) mask_type_match_variant_ident: Ident,
pub(crate) match_variant_ident: Ident, pub(crate) match_variant_ident: Ident,
@ -38,7 +38,7 @@ impl ParsedBundle {
errors: &mut Errors, errors: &mut Errors,
field: &mut Field, field: &mut Field,
index: usize, index: usize,
) -> Option<HdlAttr<kw::flip>> { ) -> Option<HdlAttr<kw::flip, kw::hdl>> {
let Field { let Field {
attrs, attrs,
vis: _, vis: _,
@ -71,7 +71,9 @@ impl ParsedBundle {
} = item; } = item;
let mut errors = Errors::new(); let mut errors = Errors::new();
let mut options = errors let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs)) .unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
&mut attrs,
))
.unwrap_or_default(); .unwrap_or_default();
errors.ok(options.body.validate()); errors.ok(options.body.validate());
let ItemOptions { let ItemOptions {

View file

@ -3,7 +3,7 @@ use crate::{
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
ParsedType, SplitForImpl, TypesParser, WrappedInConst, ParsedType, SplitForImpl, TypesParser, WrappedInConst,
}, },
Errors, HdlAttr, PairsIterExt, kw, Errors, HdlAttr, PairsIterExt,
}; };
use proc_macro2::TokenStream; use proc_macro2::TokenStream;
use quote::{format_ident, quote_spanned, ToTokens}; use quote::{format_ident, quote_spanned, ToTokens};
@ -29,7 +29,7 @@ crate::options! {
pub(crate) struct ParsedVariantField { pub(crate) struct ParsedVariantField {
pub(crate) paren_token: Paren, pub(crate) paren_token: Paren,
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<FieldOptions>, pub(crate) options: HdlAttr<FieldOptions, kw::hdl>,
pub(crate) ty: MaybeParsed<ParsedType, Type>, pub(crate) ty: MaybeParsed<ParsedType, Type>,
pub(crate) comma_token: Option<Token![,]>, pub(crate) comma_token: Option<Token![,]>,
} }
@ -37,7 +37,7 @@ pub(crate) struct ParsedVariantField {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedVariant { pub(crate) struct ParsedVariant {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<VariantOptions>, pub(crate) options: HdlAttr<VariantOptions, kw::hdl>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) field: Option<ParsedVariantField>, pub(crate) field: Option<ParsedVariantField>,
} }
@ -119,7 +119,7 @@ impl ParsedVariant {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct ParsedEnum { pub(crate) struct ParsedEnum {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ItemOptions>, pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
pub(crate) vis: Visibility, pub(crate) vis: Visibility,
pub(crate) enum_token: Token![enum], pub(crate) enum_token: Token![enum],
pub(crate) ident: Ident, pub(crate) ident: Ident,
@ -142,7 +142,9 @@ impl ParsedEnum {
} = item; } = item;
let mut errors = Errors::new(); let mut errors = Errors::new();
let mut options = errors let mut options = errors
.unwrap_or_default(HdlAttr::<ItemOptions>::parse_and_take_attr(&mut attrs)) .unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
&mut attrs,
))
.unwrap_or_default(); .unwrap_or_default();
errors.ok(options.body.validate()); errors.ok(options.body.validate());
let ItemOptions { let ItemOptions {

View file

@ -1745,7 +1745,7 @@ impl<T: ParseTypes<I>, I, P: Clone> ParseTypes<Punctuated<I, P>> for Punctuated<
pub(crate) enum UnparsedGenericParam { pub(crate) enum UnparsedGenericParam {
Type { Type {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
options: HdlAttr<TypeParamOptions>, options: HdlAttr<TypeParamOptions, kw::hdl>,
ident: Ident, ident: Ident,
colon_token: Token![:], colon_token: Token![:],
bounds: ParsedBounds, bounds: ParsedBounds,
@ -1753,7 +1753,7 @@ pub(crate) enum UnparsedGenericParam {
}, },
Const { Const {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
options: HdlAttr<ConstParamOptions>, options: HdlAttr<ConstParamOptions, kw::hdl>,
const_token: Token![const], const_token: Token![const],
ident: Ident, ident: Ident,
colon_token: Token![:], colon_token: Token![:],
@ -2278,7 +2278,7 @@ impl ParsedBound {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedTypeParam { pub(crate) struct ParsedTypeParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<TypeParamOptions>, pub(crate) options: HdlAttr<TypeParamOptions, kw::hdl>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
pub(crate) bounds: ParsedTypeBounds, pub(crate) bounds: ParsedTypeBounds,
@ -2312,7 +2312,7 @@ impl ToTokens for ParsedTypeParam {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedSizeTypeParam { pub(crate) struct ParsedSizeTypeParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<TypeParamOptions>, pub(crate) options: HdlAttr<TypeParamOptions, kw::hdl>,
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
pub(crate) bounds: ParsedSizeTypeBounds, pub(crate) bounds: ParsedSizeTypeBounds,
@ -2356,7 +2356,7 @@ pub(crate) struct ParsedConstParamWhereBounds {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct ParsedConstParam { pub(crate) struct ParsedConstParam {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) options: HdlAttr<ConstParamOptions>, pub(crate) options: HdlAttr<ConstParamOptions, kw::hdl>,
pub(crate) const_token: Token![const], pub(crate) const_token: Token![const],
pub(crate) ident: Ident, pub(crate) ident: Ident,
pub(crate) colon_token: Token![:], pub(crate) colon_token: Token![:],
@ -2413,7 +2413,7 @@ impl ParsedGenericParam {
} }
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone, Default)]
pub(crate) struct ParsedGenerics { pub(crate) struct ParsedGenerics {
pub(crate) lt_token: Option<Token![<]>, pub(crate) lt_token: Option<Token![<]>,
pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>, pub(crate) params: Punctuated<ParsedGenericParam, Token![,]>,
@ -2863,9 +2863,11 @@ impl ParsedGenerics {
let (input_param, punct) = input_param.into_tuple(); let (input_param, punct) = input_param.into_tuple();
let (unparsed_param, late_parsed_param) = match input_param { let (unparsed_param, late_parsed_param) = match input_param {
GenericParam::Lifetime(param) => { GenericParam::Lifetime(param) => {
errors.unwrap_or_default(HdlAttr::<LifetimeParamOptions>::parse_and_take_attr( errors.unwrap_or_default(
HdlAttr::<LifetimeParamOptions, kw::hdl>::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]");
continue; continue;
} }
@ -2879,7 +2881,9 @@ 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(attrs)) .unwrap_or_default(
HdlAttr::<TypeParamOptions, kw::hdl>::parse_and_take_attr(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() {
@ -2917,7 +2921,9 @@ impl ParsedGenerics {
default, default,
}) => { }) => {
let options = errors let options = errors
.unwrap_or_default(HdlAttr::<ConstParamOptions>::parse_and_take_attr(attrs)) .unwrap_or_default(
HdlAttr::<ConstParamOptions, kw::hdl>::parse_and_take_attr(attrs),
)
.unwrap_or_default(); .unwrap_or_default();
if let Some(default) = default { if let Some(default) = default {
let _ = eq_token; let _ = eq_token;

View file

@ -9,7 +9,8 @@ use syn::{
parse::{Parse, ParseStream, Parser}, parse::{Parse, ParseStream, Parser},
parse_quote, parse_quote,
punctuated::Pair, punctuated::Pair,
AttrStyle, Attribute, Error, Item, Token, spanned::Spanned,
AttrStyle, Attribute, Error, Item, ItemFn, Token,
}; };
mod fold; mod fold;
@ -17,8 +18,20 @@ mod hdl_bundle;
mod hdl_enum; mod hdl_enum;
mod hdl_type_common; mod hdl_type_common;
mod module; mod module;
//mod value_derive_common;
//mod value_derive_struct; pub(crate) trait CustomToken:
Copy
+ Spanned
+ ToTokens
+ std::fmt::Debug
+ Eq
+ std::hash::Hash
+ Default
+ quote::IdentFragment
+ Parse
{
const IDENT_STR: &'static str;
}
mod kw { mod kw {
pub(crate) use syn::token::Extern as extern_; pub(crate) use syn::token::Extern as extern_;
@ -38,6 +51,10 @@ mod kw {
} }
crate::fold::no_op_fold!($kw); crate::fold::no_op_fold!($kw);
impl crate::CustomToken for $kw {
const IDENT_STR: &'static str = stringify!($kw);
}
}; };
} }
@ -46,7 +63,9 @@ mod kw {
custom_keyword!(custom_bounds); custom_keyword!(custom_bounds);
custom_keyword!(flip); custom_keyword!(flip);
custom_keyword!(hdl); custom_keyword!(hdl);
custom_keyword!(hdl_module);
custom_keyword!(input); custom_keyword!(input);
custom_keyword!(incomplete_wire);
custom_keyword!(instance); custom_keyword!(instance);
custom_keyword!(m); custom_keyword!(m);
custom_keyword!(memory); custom_keyword!(memory);
@ -68,34 +87,34 @@ mod kw {
type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676 type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) struct HdlAttr<T> { pub(crate) struct HdlAttr<T, KW> {
pub(crate) pound_token: Pound, pub(crate) pound_token: Pound,
pub(crate) style: AttrStyle, pub(crate) style: AttrStyle,
pub(crate) bracket_token: syn::token::Bracket, pub(crate) bracket_token: syn::token::Bracket,
pub(crate) hdl: kw::hdl, pub(crate) kw: KW,
pub(crate) paren_token: Option<syn::token::Paren>, pub(crate) paren_token: Option<syn::token::Paren>,
pub(crate) body: T, pub(crate) body: T,
} }
crate::fold::impl_fold! { crate::fold::impl_fold! {
struct HdlAttr<T,> { struct HdlAttr<T, KW,> {
pound_token: Pound, pound_token: Pound,
style: AttrStyle, style: AttrStyle,
bracket_token: syn::token::Bracket, bracket_token: syn::token::Bracket,
hdl: kw::hdl, kw: KW,
paren_token: Option<syn::token::Paren>, paren_token: Option<syn::token::Paren>,
body: T, body: T,
} }
} }
#[allow(dead_code)] #[allow(dead_code)]
impl<T> HdlAttr<T> { impl<T, KW> HdlAttr<T, KW> {
pub(crate) fn split_body(self) -> (HdlAttr<()>, T) { pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -104,19 +123,19 @@ impl<T> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body: (), body: (),
}, },
body, body,
) )
} }
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2> { pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body: _, body: _,
} = self; } = self;
@ -124,17 +143,20 @@ impl<T> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body, body,
} }
} }
pub(crate) fn as_ref(&self) -> HdlAttr<&T> { pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW>
where
KW: Clone,
{
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, ref kw,
paren_token, paren_token,
ref body, ref body,
} = *self; } = *self;
@ -142,17 +164,20 @@ impl<T> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw: kw.clone(),
paren_token, paren_token,
body, body,
} }
} }
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(self, f: F) -> Result<HdlAttr<R>, E> { pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(
self,
f: F,
) -> Result<HdlAttr<R, KW>, E> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -160,17 +185,17 @@ impl<T> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body: f(body)?, body: f(body)?,
}) })
} }
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R> { pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> {
let Self { let Self {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body, body,
} = self; } = self;
@ -178,7 +203,7 @@ impl<T> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body: f(body), body: f(body),
} }
@ -186,31 +211,32 @@ impl<T> HdlAttr<T> {
fn to_attr(&self) -> Attribute fn to_attr(&self) -> Attribute
where where
T: ToTokens, T: ToTokens,
KW: ToTokens,
{ {
parse_quote! { #self } parse_quote! { #self }
} }
} }
impl<T: Default> Default for HdlAttr<T> { impl<T: Default, KW: Default> Default for HdlAttr<T, KW> {
fn default() -> Self { fn default() -> Self {
T::default().into() T::default().into()
} }
} }
impl<T> From<T> for HdlAttr<T> { impl<T, KW: Default> From<T> for HdlAttr<T, KW> {
fn from(body: T) -> Self { fn from(body: T) -> Self {
HdlAttr { HdlAttr {
pound_token: Default::default(), pound_token: Default::default(),
style: AttrStyle::Outer, style: AttrStyle::Outer,
bracket_token: Default::default(), bracket_token: Default::default(),
hdl: Default::default(), kw: Default::default(),
paren_token: Default::default(), paren_token: Default::default(),
body, body,
} }
} }
} }
impl<T: ToTokens> ToTokens for HdlAttr<T> { impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
self.pound_token.to_tokens(tokens); self.pound_token.to_tokens(tokens);
match self.style { match self.style {
@ -218,7 +244,7 @@ impl<T: ToTokens> ToTokens for HdlAttr<T> {
AttrStyle::Outer => {} AttrStyle::Outer => {}
}; };
self.bracket_token.surround(tokens, |tokens| { self.bracket_token.surround(tokens, |tokens| {
self.hdl.to_tokens(tokens); self.kw.to_tokens(tokens);
match self.paren_token { match self.paren_token {
Some(paren_token) => { Some(paren_token) => {
paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens)) paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
@ -226,7 +252,7 @@ impl<T: ToTokens> ToTokens for HdlAttr<T> {
None => { None => {
let body = self.body.to_token_stream(); let body = self.body.to_token_stream();
if !body.is_empty() { if !body.is_empty() {
syn::token::Paren(self.hdl.span) syn::token::Paren(self.kw.span())
.surround(tokens, |tokens| tokens.extend([body])); .surround(tokens, |tokens| tokens.extend([body]));
} }
} }
@ -235,18 +261,24 @@ impl<T: ToTokens> ToTokens for HdlAttr<T> {
} }
} }
fn is_hdl_attr(attr: &Attribute) -> bool { fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool {
attr.path().is_ident("hdl") attr.path().is_ident(KW::IDENT_STR)
} }
impl<T: Parse> HdlAttr<T> { impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> { fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>>
where
KW: ToTokens,
{
let mut retval = None; let mut retval = None;
let mut errors = Errors::new(); let mut errors = Errors::new();
attrs.retain(|attr| { attrs.retain(|attr| {
if is_hdl_attr(attr) { if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
if retval.is_some() { if retval.is_some() {
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); errors.push(Error::new_spanned(
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
} }
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
false false
@ -257,13 +289,19 @@ impl<T: Parse> HdlAttr<T> {
errors.finish()?; errors.finish()?;
Ok(retval) Ok(retval)
} }
fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>> { fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>>
where
KW: ToTokens,
{
let mut retval = None; let mut retval = None;
let mut errors = Errors::new(); let mut errors = Errors::new();
for attr in attrs { for attr in attrs {
if is_hdl_attr(attr) { if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
if retval.is_some() { if retval.is_some() {
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute")); errors.push(Error::new_spanned(
attr,
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
));
} }
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v))); errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
} }
@ -284,7 +322,7 @@ impl<T: Parse> HdlAttr<T> {
) -> syn::Result<Self> { ) -> syn::Result<Self> {
let bracket_content; let bracket_content;
let bracket_token = bracketed!(bracket_content in input); let bracket_token = bracketed!(bracket_content in input);
let hdl = bracket_content.parse()?; let kw = bracket_content.parse()?;
let paren_content; let paren_content;
let body; let body;
let paren_token; let paren_token;
@ -305,7 +343,7 @@ impl<T: Parse> HdlAttr<T> {
pound_token, pound_token,
style, style,
bracket_token, bracket_token,
hdl, kw,
paren_token, paren_token,
body, body,
}) })
@ -852,25 +890,31 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
} }
} }
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
let options = syn::parse2::<module::ConfigOptions>(attr)?; let func = module::ModuleFn::parse_from_fn(item)?;
let options = HdlAttr::from(options); let options = func.config_options();
let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
let mut contents = func.generate(); let mut contents = func.generate();
if options.body.outline_generated.is_some() { if options.outline_generated.is_some() {
contents = outline_generated(contents, "module-"); contents = outline_generated(contents, "module-");
} }
Ok(contents) Ok(contents)
} }
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let kw = kw::hdl_module::default();
hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
}
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> { pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
let item = syn::parse2::<Item>(quote! { #[hdl(#attr)] #item })?; let kw = kw::hdl::default();
let item = syn::parse2::<Item>(quote! { #[#kw(#attr)] #item })?;
match item { match item {
Item::Enum(item) => hdl_enum::hdl_enum(item), Item::Enum(item) => hdl_enum::hdl_enum(item),
Item::Struct(item) => hdl_bundle::hdl_bundle(item), Item::Struct(item) => hdl_bundle::hdl_bundle(item),
Item::Fn(item) => hdl_module_impl(item),
_ => Err(syn::Error::new( _ => Err(syn::Error::new(
Span::call_site(), Span::call_site(),
"top-level #[hdl] can only be used on structs or enums", "top-level #[hdl] can only be used on structs, enums, or functions",
)), )),
} }
} }

View file

@ -2,6 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
hdl_type_common::{ParsedGenerics, SplitForImpl}, hdl_type_common::{ParsedGenerics, SplitForImpl},
kw,
module::transform_body::{HdlLet, HdlLetKindIO}, module::transform_body::{HdlLet, HdlLetKindIO},
options, Errors, HdlAttr, PairsIterExt, options, Errors, HdlAttr, PairsIterExt,
}; };
@ -9,7 +10,6 @@ use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned, ToTokens}; use quote::{format_ident, quote, quote_spanned, ToTokens};
use std::collections::HashSet; use std::collections::HashSet;
use syn::{ use syn::{
parse::{Parse, ParseStream},
parse_quote, parse_quote,
visit::{visit_pat, Visit}, visit::{visit_pat, Visit},
Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct, Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct,
@ -59,9 +59,9 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>; pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
pub(crate) struct ModuleFn { struct ModuleFnModule {
attrs: Vec<Attribute>, attrs: Vec<Attribute>,
config_options: HdlAttr<ConfigOptions>, config_options: HdlAttr<ConfigOptions, kw::hdl_module>,
module_kind: ModuleKind, module_kind: ModuleKind,
vis: Visibility, vis: Visibility,
sig: Signature, sig: Signature,
@ -70,6 +70,26 @@ pub(crate) struct ModuleFn {
the_struct: TokenStream, the_struct: TokenStream,
} }
enum ModuleFnImpl {
Module(ModuleFnModule),
Fn {
attrs: Vec<Attribute>,
config_options: HdlAttr<ConfigOptions, kw::hdl>,
vis: Visibility,
sig: Signature,
block: Box<Block>,
},
}
options! {
pub(crate) enum HdlOrHdlModule {
Hdl(hdl),
HdlModule(hdl_module),
}
}
pub(crate) struct ModuleFn(ModuleFnImpl);
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub(crate) enum ModuleKind { pub(crate) enum ModuleKind {
Extern, Extern,
@ -89,14 +109,25 @@ impl Visit<'_> for ContainsSkippedIdent<'_> {
} }
} }
impl Parse for ModuleFn { impl ModuleFn {
fn parse(input: ParseStream) -> syn::Result<Self> { pub(crate) fn config_options(&self) -> ConfigOptions {
let (ModuleFnImpl::Module(ModuleFnModule {
config_options: HdlAttr { body, .. },
..
})
| ModuleFnImpl::Fn {
config_options: HdlAttr { body, .. },
..
}) = &self.0;
body.clone()
}
pub(crate) fn parse_from_fn(item: ItemFn) -> syn::Result<Self> {
let ItemFn { let ItemFn {
mut attrs, mut attrs,
vis, vis,
mut sig, mut sig,
block, block,
} = input.parse()?; } = item;
let Signature { let Signature {
ref constness, ref constness,
ref asyncness, ref asyncness,
@ -111,17 +142,33 @@ impl Parse for ModuleFn {
ref output, ref output,
} = sig; } = sig;
let mut errors = Errors::new(); let mut errors = Errors::new();
let config_options = errors let Some(mut config_options) =
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs)) errors.unwrap_or_default(
.unwrap_or_default(); HdlAttr::<ConfigOptions, HdlOrHdlModule>::parse_and_take_attr(&mut attrs),
)
else {
errors.error(sig.ident, "missing #[hdl] or #[hdl_module] attribute");
errors.finish()?;
unreachable!();
};
let ConfigOptions { let ConfigOptions {
outline_generated: _, outline_generated: _,
extern_, extern_,
} = config_options.body; } = config_options.body;
let module_kind = match extern_ { let module_kind = match (config_options.kw, extern_) {
Some(_) => ModuleKind::Extern, (HdlOrHdlModule::Hdl(_), None) => None,
None => ModuleKind::Normal, (HdlOrHdlModule::Hdl(_), Some(extern2)) => {
config_options.body.extern_ = None;
errors.error(
extern2.0,
"extern can only be used as #[hdl_module(extern)]",
);
None
}
(HdlOrHdlModule::HdlModule(_), None) => Some(ModuleKind::Normal),
(HdlOrHdlModule::HdlModule(_), Some(_)) => Some(ModuleKind::Extern),
}; };
if let HdlOrHdlModule::HdlModule(_) = config_options.kw {
for fn_arg in inputs { for fn_arg in inputs {
match fn_arg { match fn_arg {
FnArg::Receiver(_) => { FnArg::Receiver(_) => {
@ -149,20 +196,24 @@ impl Parse for ModuleFn {
if let Some(abi) = abi { if let Some(abi) = abi {
errors.push(syn::Error::new_spanned(abi, "extern not allowed here")); errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
} }
}
let mut skipped_idents = HashSet::new(); let mut skipped_idents = HashSet::new();
let struct_generic_params = generics let struct_generic_params = generics
.params .params
.pairs_mut() .pairs_mut()
.filter_map_pair_value_mut(|v| match v { .filter_map_pair_value_mut(|v| match v {
GenericParam::Lifetime(LifetimeParam { attrs, .. }) => { GenericParam::Lifetime(LifetimeParam { attrs, .. }) => {
errors errors.unwrap_or_default(
.unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs)); HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs),
);
None None
} }
GenericParam::Type(TypeParam { attrs, ident, .. }) GenericParam::Type(TypeParam { attrs, ident, .. })
| GenericParam::Const(ConstParam { attrs, ident, .. }) => { | GenericParam::Const(ConstParam { attrs, ident, .. }) => {
if errors if errors
.unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs)) .unwrap_or_default(
HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs),
)
.is_some() .is_some()
{ {
skipped_idents.insert(ident.clone()); skipped_idents.insert(ident.clone());
@ -176,6 +227,7 @@ impl Parse for ModuleFn {
let struct_where_clause = generics let struct_where_clause = generics
.where_clause .where_clause
.as_mut() .as_mut()
.filter(|_| matches!(config_options.kw, HdlOrHdlModule::HdlModule(_)))
.map(|where_clause| WhereClause { .map(|where_clause| WhereClause {
where_token: where_clause.where_token, where_token: where_clause.where_token,
predicates: where_clause predicates: where_clause
@ -198,7 +250,8 @@ impl Parse for ModuleFn {
}) })
.collect(), .collect(),
}); });
let struct_generics = Generics { let struct_generics = if let HdlOrHdlModule::HdlModule(_) = config_options.kw {
let mut struct_generics = Generics {
lt_token: generics.lt_token, lt_token: generics.lt_token,
params: struct_generic_params, params: struct_generic_params,
gt_token: generics.gt_token, gt_token: generics.gt_token,
@ -213,7 +266,10 @@ impl Parse for ModuleFn {
"return type not allowed here", "return type not allowed here",
)); ));
} }
let struct_generics = errors.ok(ParsedGenerics::parse(&mut { struct_generics })); errors.ok(ParsedGenerics::parse(&mut struct_generics))
} else {
Some(ParsedGenerics::default())
};
let body_results = struct_generics.as_ref().and_then(|struct_generics| { let body_results = struct_generics.as_ref().and_then(|struct_generics| {
errors.ok(transform_body::transform_body( errors.ok(transform_body::transform_body(
module_kind, module_kind,
@ -224,6 +280,47 @@ 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 config_options = match config_options {
HdlAttr {
pound_token,
style,
bracket_token,
kw: HdlOrHdlModule::Hdl((kw,)),
paren_token,
body,
} => {
debug_assert!(io.is_empty());
return Ok(Self(ModuleFnImpl::Fn {
attrs,
config_options: HdlAttr {
pound_token,
style,
bracket_token,
kw,
paren_token,
body,
},
vis,
sig,
block,
}));
}
HdlAttr {
pound_token,
style,
bracket_token,
kw: HdlOrHdlModule::HdlModule((kw,)),
paren_token,
body,
} => HdlAttr {
pound_token,
style,
bracket_token,
kw,
paren_token,
body,
},
};
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_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause }; let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
@ -259,22 +356,22 @@ impl Parse for ModuleFn {
} }
}; };
let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?; let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?;
Ok(Self { Ok(Self(ModuleFnImpl::Module(ModuleFnModule {
attrs, attrs,
config_options, config_options,
module_kind, module_kind: module_kind.unwrap(),
vis, vis,
sig, sig,
block, block,
struct_generics, struct_generics,
the_struct, the_struct,
}) })))
} }
} }
impl ModuleFn { impl ModuleFn {
pub(crate) fn generate(self) -> TokenStream { pub(crate) fn generate(self) -> TokenStream {
let Self { let ModuleFnModule {
attrs, attrs,
config_options, config_options,
module_kind, module_kind,
@ -283,7 +380,28 @@ impl ModuleFn {
block, block,
struct_generics, struct_generics,
the_struct, the_struct,
} = self; } = match self.0 {
ModuleFnImpl::Module(v) => v,
ModuleFnImpl::Fn {
attrs,
config_options,
vis,
sig,
block,
} => {
let ConfigOptions {
outline_generated: _,
extern_: _,
} = config_options.body;
return ItemFn {
attrs,
vis,
sig,
block,
}
.into_token_stream();
}
};
let ConfigOptions { let ConfigOptions {
outline_generated: _, outline_generated: _,
extern_: _, extern_: _,

View file

@ -34,6 +34,7 @@ options! {
Instance(instance), Instance(instance),
RegBuilder(reg_builder), RegBuilder(reg_builder),
Wire(wire), Wire(wire),
IncompleteWire(incomplete_wire),
Memory(memory), Memory(memory),
MemoryArray(memory_array), MemoryArray(memory_array),
MemoryWithInit(memory_with_init), MemoryWithInit(memory_with_init),
@ -533,6 +534,41 @@ impl HdlLetKindToTokens for HdlLetKindWire {
} }
} }
options! {
pub(crate) enum LetFnKindIncomplete {
IncompleteWire(incomplete_wire),
}
}
#[derive(Clone, Debug)]
pub(crate) struct HdlLetKindIncomplete {
pub(crate) kind: LetFnKindIncomplete,
pub(crate) paren: Paren,
}
impl ParseTypes<Self> for HdlLetKindIncomplete {
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
Ok(input.clone())
}
}
impl_fold! {
struct HdlLetKindIncomplete<> {
kind: LetFnKindIncomplete,
paren: Paren,
}
}
impl HdlLetKindToTokens for HdlLetKindIncomplete {
fn ty_to_tokens(&self, _tokens: &mut TokenStream) {}
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
let Self { kind, paren } = self;
kind.to_tokens(tokens);
paren.surround(tokens, |_| {});
}
}
options! { options! {
pub(crate) enum MemoryFnName { pub(crate) enum MemoryFnName {
Memory(memory), Memory(memory),
@ -697,6 +733,7 @@ impl HdlLetKindMemory {
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub(crate) enum HdlLetKind<IOType = ParsedType> { pub(crate) enum HdlLetKind<IOType = ParsedType> {
IO(HdlLetKindIO<ModuleIOKind, IOType>), IO(HdlLetKindIO<ModuleIOKind, IOType>),
Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire), Wire(HdlLetKindWire),
@ -706,6 +743,7 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> {
impl_fold! { impl_fold! {
enum HdlLetKind<IOType,> { enum HdlLetKind<IOType,> {
IO(HdlLetKindIO<ModuleIOKind, IOType>), IO(HdlLetKindIO<ModuleIOKind, IOType>),
Incomplete(HdlLetKindIncomplete),
Instance(HdlLetKindInstance), Instance(HdlLetKindInstance),
RegBuilder(HdlLetKindRegBuilder), RegBuilder(HdlLetKindRegBuilder),
Wire(HdlLetKindWire), Wire(HdlLetKindWire),
@ -720,6 +758,9 @@ impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
) -> Result<Self, ParseFailed> { ) -> Result<Self, ParseFailed> {
match input { match input {
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO), HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
HdlLetKind::Incomplete(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
}
HdlLetKind::Instance(input) => { HdlLetKind::Instance(input) => {
ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance) ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance)
} }
@ -871,6 +912,20 @@ impl HdlLetKindParse for HdlLetKind<Type> {
ty_expr: paren_contents.call(parse_optional_fn_arg)?, ty_expr: paren_contents.call(parse_optional_fn_arg)?,
})) }))
} }
LetFnKind::IncompleteWire(incomplete_wire) => {
if let Some(parsed_ty) = parsed_ty {
return Err(Error::new_spanned(
parsed_ty.1,
"type annotation not allowed for incomplete_wire",
));
}
check_empty_m_dot(m_dot, kind)?;
let _paren_contents;
Ok(Self::Incomplete(HdlLetKindIncomplete {
kind: LetFnKindIncomplete::IncompleteWire(incomplete_wire),
paren: parenthesized!(_paren_contents in input),
}))
}
LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse( LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse(
input, input,
parsed_ty, parsed_ty,
@ -903,6 +958,7 @@ impl HdlLetKindToTokens for HdlLetKind {
fn ty_to_tokens(&self, tokens: &mut TokenStream) { fn ty_to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
HdlLetKind::IO(v) => v.ty_to_tokens(tokens), HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens),
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens), HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
HdlLetKind::Wire(v) => v.ty_to_tokens(tokens), HdlLetKind::Wire(v) => v.ty_to_tokens(tokens),
@ -913,6 +969,7 @@ impl HdlLetKindToTokens for HdlLetKind {
fn expr_to_tokens(&self, tokens: &mut TokenStream) { fn expr_to_tokens(&self, tokens: &mut TokenStream) {
match self { match self {
HdlLetKind::IO(v) => v.expr_to_tokens(tokens), HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens),
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens), HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens), HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
HdlLetKind::Wire(v) => v.expr_to_tokens(tokens), HdlLetKind::Wire(v) => v.expr_to_tokens(tokens),
@ -925,7 +982,7 @@ with_debug_clone_and_fold! {
#[allow(dead_code)] #[allow(dead_code)]
pub(crate) struct HdlLet<Kind = HdlLetKind> { pub(crate) struct HdlLet<Kind = HdlLetKind> {
pub(crate) attrs: Vec<Attribute>, pub(crate) attrs: Vec<Attribute>,
pub(crate) hdl_attr: HdlAttr<Nothing>, pub(crate) hdl_attr: HdlAttr<Nothing, kw::hdl>,
pub(crate) let_token: Token![let], pub(crate) let_token: Token![let],
pub(crate) mut_token: Option<Token![mut]>, pub(crate) mut_token: Option<Token![mut]>,
pub(crate) name: Ident, pub(crate) name: Ident,
@ -1112,7 +1169,7 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
} }
struct Visitor<'a> { struct Visitor<'a> {
module_kind: ModuleKind, module_kind: Option<ModuleKind>,
errors: Errors, errors: Errors,
io: Vec<ModuleIO>, io: Vec<ModuleIO>,
block_depth: usize, block_depth: usize,
@ -1120,22 +1177,33 @@ struct Visitor<'a> {
} }
impl Visitor<'_> { impl Visitor<'_> {
fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> { fn take_hdl_attr<T: Parse>(
&mut self,
attrs: &mut Vec<Attribute>,
) -> Option<HdlAttr<T, kw::hdl>> {
self.errors.unwrap_or( self.errors.unwrap_or(
HdlAttr::parse_and_take_attr(attrs), HdlAttr::parse_and_take_attr(attrs),
Some(syn::parse2::<T>(quote! {}).unwrap().into()), Some(syn::parse2::<T>(quote! {}).unwrap().into()),
) )
} }
fn require_normal_module(&mut self, spanned: impl ToTokens) { fn require_normal_module_or_fn(&mut self, spanned: impl ToTokens) {
match self.module_kind { match self.module_kind {
ModuleKind::Extern => { Some(ModuleKind::Extern) => {
self.errors self.errors
.error(spanned, "not allowed in #[hdl_module(extern)]"); .error(spanned, "not allowed in #[hdl_module(extern)]");
} }
ModuleKind::Normal => {} Some(ModuleKind::Normal) | None => {}
} }
} }
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing>, expr_if: ExprIf) -> Expr { fn require_module(&mut self, spanned: impl ToTokens) {
match self.module_kind {
None => {
self.errors.error(spanned, "not allowed in #[hdl] fn");
}
Some(_) => {}
}
}
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing, kw::hdl>, expr_if: ExprIf) -> Expr {
let ExprIf { let ExprIf {
attrs, attrs,
if_token, if_token,
@ -1143,7 +1211,7 @@ impl Visitor<'_> {
then_branch, then_branch,
else_branch, else_branch,
} = expr_if; } = expr_if;
self.require_normal_module(if_token); self.require_normal_module_or_fn(if_token);
let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr { let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr {
Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if), Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if),
expr => expr, expr => expr,
@ -1208,11 +1276,12 @@ impl Visitor<'_> {
.to_tokens(expr); .to_tokens(expr);
}); });
let mut attrs = hdl_let.attrs.clone(); let mut attrs = hdl_let.attrs.clone();
self.require_module(kind);
match self.module_kind { match self.module_kind {
ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=> Some(ModuleKind::Extern) => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=>
#[allow(unused_variables)] #[allow(unused_variables)]
}), }),
ModuleKind::Normal => {} Some(ModuleKind::Normal) | None => {}
} }
let let_stmt = Local { let let_stmt = Local {
attrs, attrs,
@ -1249,7 +1318,7 @@ impl Visitor<'_> {
}, },
semi_token, semi_token,
} = hdl_let; } = hdl_let;
self.require_normal_module(instance); self.require_normal_module_or_fn(instance);
let mut expr = instance.to_token_stream(); let mut expr = instance.to_token_stream();
paren.surround(&mut expr, |expr| { paren.surround(&mut expr, |expr| {
let name_str = ImplicitName { let name_str = ImplicitName {
@ -1276,7 +1345,7 @@ impl Visitor<'_> {
fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local { fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let reg_builder = hdl_let.kind.reg_builder; let reg_builder = hdl_let.kind.reg_builder;
self.require_normal_module(reg_builder); self.require_normal_module_or_fn(reg_builder);
let mut expr = reg_builder.to_token_stream(); let mut expr = reg_builder.to_token_stream();
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| { hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
let name_str = ImplicitName { let name_str = ImplicitName {
@ -1327,7 +1396,7 @@ impl Visitor<'_> {
fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local { fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let wire = hdl_let.kind.wire; let wire = hdl_let.kind.wire;
self.require_normal_module(wire); self.require_normal_module_or_fn(wire);
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span()); let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span());
let mut expr = wire.to_token_stream(); let mut expr = wire.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| { hdl_let.kind.paren.surround(&mut expr, |expr| {
@ -1357,11 +1426,36 @@ impl Visitor<'_> {
semi_token: hdl_let.semi_token, semi_token: hdl_let.semi_token,
} }
} }
fn process_hdl_let_incomplete(&mut self, hdl_let: HdlLet<HdlLetKindIncomplete>) -> Local {
let name = &hdl_let.name;
let kind = hdl_let.kind.kind;
self.require_normal_module_or_fn(kind);
let mut expr = kind.to_token_stream();
hdl_let.kind.paren.surround(&mut expr, |expr| {
ImplicitName {
name,
span: name.span(),
}
.to_tokens(expr);
});
let mut_token = &hdl_let.mut_token;
Local {
attrs: hdl_let.attrs.clone(),
let_token: hdl_let.let_token,
pat: parse_quote! { #mut_token #name },
init: Some(LocalInit {
eq_token: hdl_let.eq_token,
expr: parse_quote! { #expr },
diverge: None,
}),
semi_token: hdl_let.semi_token,
}
}
fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local { fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local {
let name = &hdl_let.name; let name = &hdl_let.name;
let memory_fn = hdl_let.kind.memory_fn; let memory_fn = hdl_let.kind.memory_fn;
let memory_fn_name = memory_fn.name(); let memory_fn_name = memory_fn.name();
self.require_normal_module(memory_fn_name); self.require_normal_module_or_fn(memory_fn_name);
let mut expr = memory_fn_name.to_token_stream(); let mut expr = memory_fn_name.to_token_stream();
let (paren, arg) = match memory_fn { let (paren, arg) = match memory_fn {
MemoryFn::Memory { MemoryFn::Memory {
@ -1426,6 +1520,7 @@ impl Visitor<'_> {
} }
the_match! { the_match! {
IO => process_hdl_let_io, IO => process_hdl_let_io,
Incomplete => process_hdl_let_incomplete,
Instance => process_hdl_let_instance, Instance => process_hdl_let_instance,
RegBuilder => process_hdl_let_reg_builder, RegBuilder => process_hdl_let_reg_builder,
Wire => process_hdl_let_wire, Wire => process_hdl_let_wire,
@ -1543,7 +1638,7 @@ impl Fold for Visitor<'_> {
} }
fn fold_attribute(&mut self, attr: Attribute) -> Attribute { fn fold_attribute(&mut self, attr: Attribute) -> Attribute {
if is_hdl_attr(&attr) { if is_hdl_attr::<kw::hdl>(&attr) {
self.errors self.errors
.error(&attr, "#[hdl] attribute not supported here"); .error(&attr, "#[hdl] attribute not supported here");
} }
@ -1610,8 +1705,9 @@ impl Fold for Visitor<'_> {
fn fold_local(&mut self, let_stmt: Local) -> Local { fn fold_local(&mut self, let_stmt: Local) -> Local {
match self match self
.errors .errors
.ok(HdlAttr::<Nothing>::parse_and_leave_attr(&let_stmt.attrs)) .ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr(
{ &let_stmt.attrs,
)) {
None => return empty_let(), None => return empty_let(),
Some(None) => return fold_local(self, let_stmt), Some(None) => return fold_local(self, let_stmt),
Some(Some(HdlAttr { .. })) => {} Some(Some(HdlAttr { .. })) => {}
@ -1646,7 +1742,7 @@ impl Fold for Visitor<'_> {
} }
pub(crate) fn transform_body( pub(crate) fn transform_body(
module_kind: ModuleKind, module_kind: Option<ModuleKind>,
mut body: Box<Block>, mut body: Box<Block>,
parsed_generics: &ParsedGenerics, parsed_generics: &ParsedGenerics,
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> { ) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {

View file

@ -1,6 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{module::transform_body::Visitor, HdlAttr}; use crate::{kw, module::transform_body::Visitor, HdlAttr};
use quote::{format_ident, quote_spanned}; use quote::{format_ident, quote_spanned};
use syn::{ use syn::{
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath, parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
@ -10,10 +10,10 @@ use syn::{
impl Visitor<'_> { impl Visitor<'_> {
pub(crate) fn process_hdl_array( pub(crate) fn process_hdl_array(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing>, hdl_attr: HdlAttr<Nothing, kw::hdl>,
mut expr_array: ExprArray, mut expr_array: ExprArray,
) -> Expr { ) -> Expr {
self.require_normal_module(hdl_attr); self.require_normal_module_or_fn(hdl_attr);
for elem in &mut expr_array.elems { for elem in &mut expr_array.elems {
*elem = parse_quote_spanned! {elem.span()=> *elem = parse_quote_spanned! {elem.span()=>
::fayalite::expr::ToExpr::to_expr(&(#elem)) ::fayalite::expr::ToExpr::to_expr(&(#elem))
@ -23,10 +23,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_repeat( pub(crate) fn process_hdl_repeat(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing>, hdl_attr: HdlAttr<Nothing, kw::hdl>,
mut expr_repeat: ExprRepeat, mut expr_repeat: ExprRepeat,
) -> Expr { ) -> Expr {
self.require_normal_module(hdl_attr); self.require_normal_module_or_fn(hdl_attr);
let repeated_value = &expr_repeat.expr; let repeated_value = &expr_repeat.expr;
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=> *expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
::fayalite::expr::ToExpr::to_expr(&(#repeated_value)) ::fayalite::expr::ToExpr::to_expr(&(#repeated_value))
@ -35,10 +35,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_struct( pub(crate) fn process_hdl_struct(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing>, hdl_attr: HdlAttr<Nothing, kw::hdl>,
expr_struct: ExprStruct, expr_struct: ExprStruct,
) -> Expr { ) -> Expr {
self.require_normal_module(&hdl_attr); self.require_normal_module_or_fn(&hdl_attr);
let name_span = expr_struct.path.segments.last().unwrap().ident.span(); let name_span = expr_struct.path.segments.last().unwrap().ident.span();
let builder_ident = format_ident!("__builder", span = name_span); let builder_ident = format_ident!("__builder", span = name_span);
let empty_builder = if expr_struct.qself.is_some() let empty_builder = if expr_struct.qself.is_some()
@ -91,10 +91,10 @@ impl Visitor<'_> {
} }
pub(crate) fn process_hdl_tuple( pub(crate) fn process_hdl_tuple(
&mut self, &mut self,
hdl_attr: HdlAttr<Nothing>, hdl_attr: HdlAttr<Nothing, kw::hdl>,
expr_tuple: ExprTuple, expr_tuple: ExprTuple,
) -> Expr { ) -> Expr {
self.require_normal_module(hdl_attr); self.require_normal_module_or_fn(hdl_attr);
parse_quote_spanned! {expr_tuple.span()=> parse_quote_spanned! {expr_tuple.span()=>
::fayalite::expr::ToExpr::to_expr(&#expr_tuple) ::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
} }

View file

@ -2,6 +2,7 @@
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
fold::{impl_fold, DoFold}, fold::{impl_fold, DoFold},
kw,
module::transform_body::{with_debug_clone_and_fold, Visitor}, module::transform_body::{with_debug_clone_and_fold, Visitor},
Errors, HdlAttr, PairsIterExt, Errors, HdlAttr, PairsIterExt,
}; };
@ -749,7 +750,7 @@ struct HdlMatchParseState<'a> {
impl Visitor<'_> { impl Visitor<'_> {
pub(crate) fn process_hdl_match( pub(crate) fn process_hdl_match(
&mut self, &mut self,
_hdl_attr: HdlAttr<Nothing>, _hdl_attr: HdlAttr<Nothing, kw::hdl>,
expr_match: ExprMatch, expr_match: ExprMatch,
) -> Expr { ) -> Expr {
let span = expr_match.match_token.span(); let span = expr_match.match_token.span();
@ -761,7 +762,7 @@ impl Visitor<'_> {
brace_token: _, brace_token: _,
arms, arms,
} = expr_match; } = expr_match;
self.require_normal_module(match_token); self.require_normal_module_or_fn(match_token);
let mut state = HdlMatchParseState { let mut state = HdlMatchParseState {
match_span: span, match_span: span,
errors: &mut self.errors, errors: &mut self.errors,

View file

@ -7,14 +7,14 @@ use crate::{
int::Bool, int::Bool,
intern::{Intern, Interned}, intern::{Intern, Interned},
module::{ module::{
enum_match_variants_helper, EnumMatchVariantAndInactiveScopeImpl, connect, enum_match_variants_helper, incomplete_wire, wire,
EnumMatchVariantsIterImpl, Scope, EnumMatchVariantAndInactiveScopeImpl, EnumMatchVariantsIterImpl, Scope,
}, },
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties}, ty::{CanonicalType, MatchVariantAndInactiveScope, StaticType, Type, TypeProperties},
}; };
use hashbrown::HashMap; use hashbrown::HashMap;
use std::{fmt, iter::FusedIterator}; use std::{convert::Infallible, fmt, iter::FusedIterator};
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
pub struct EnumVariant { pub struct EnumVariant {
@ -364,3 +364,308 @@ pub fn HdlSome<T: Type>(value: impl ToExpr<Type = T>) -> Expr<HdlOption<T>> {
let value = value.to_expr(); let value = value.to_expr();
HdlOption[Expr::ty(value)].HdlSome(value) HdlOption[Expr::ty(value)].HdlSome(value)
} }
impl<T: Type> HdlOption<T> {
#[track_caller]
pub fn try_map<R: Type, E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<R>, E>,
) -> Result<Expr<HdlOption<R>>, E> {
Self::try_and_then(expr, |v| Ok(HdlSome(f(v)?)))
}
#[track_caller]
pub fn map<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<HdlOption<R>> {
Self::and_then(expr, |v| HdlSome(f(v)))
}
#[hdl]
#[track_caller]
pub fn try_and_then<R: Type, E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<HdlOption<R>>, E>,
) -> Result<Expr<HdlOption<R>>, E> {
// manually run match steps so we can extract the return type to construct HdlNone
type Wrap<T> = T;
#[hdl]
let mut and_then_out = incomplete_wire();
let mut iter = Self::match_variants(expr, SourceLocation::caller());
let none = iter.next().unwrap();
let some = iter.next().unwrap();
assert!(iter.next().is_none());
let (Wrap::<<Self as Type>::MatchVariant>::HdlSome(value), some_scope) =
Self::match_activate_scope(some)
else {
unreachable!();
};
let value = f(value).map_err(|e| {
and_then_out.complete(()); // avoid error
e
})?;
let and_then_out = and_then_out.complete(Expr::ty(value));
connect(and_then_out, value);
drop(some_scope);
let (Wrap::<<Self as Type>::MatchVariant>::HdlNone, none_scope) =
Self::match_activate_scope(none)
else {
unreachable!();
};
connect(and_then_out, Expr::ty(and_then_out).HdlNone());
drop(none_scope);
Ok(and_then_out)
}
#[track_caller]
pub fn and_then<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<HdlOption<R>>,
) -> Expr<HdlOption<R>> {
match Self::try_and_then(expr, |v| Ok::<_, Infallible>(f(v))) {
Ok(v) => v,
Err(e) => match e {},
}
}
#[hdl]
#[track_caller]
pub fn and<U: Type>(expr: Expr<Self>, opt_b: Expr<HdlOption<U>>) -> Expr<HdlOption<U>> {
#[hdl]
let and_out = wire(Expr::ty(opt_b));
connect(and_out, Expr::ty(opt_b).HdlNone());
#[hdl]
if let HdlSome(_) = expr {
connect(and_out, opt_b);
}
and_out
}
#[hdl]
#[track_caller]
pub fn try_filter<E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<Expr<Bool>, E>,
) -> Result<Expr<Self>, E> {
#[hdl]
let filtered = wire(Expr::ty(expr));
connect(filtered, Expr::ty(expr).HdlNone());
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
#[hdl]
if f.take().unwrap()(v)? {
connect(filtered, HdlSome(v));
}
}
Ok(filtered)
}
#[hdl]
#[track_caller]
pub fn filter(expr: Expr<Self>, f: impl FnOnce(Expr<T>) -> Expr<Bool>) -> Expr<Self> {
match Self::try_filter(expr, |v| Ok::<_, Infallible>(f(v))) {
Ok(v) => v,
Err(e) => match e {},
}
}
#[hdl]
#[track_caller]
pub fn try_inspect<E>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Result<(), E>,
) -> Result<Expr<Self>, E> {
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
f.take().unwrap()(v)?;
}
Ok(expr)
}
#[hdl]
#[track_caller]
pub fn inspect(expr: Expr<Self>, f: impl FnOnce(Expr<T>)) -> Expr<Self> {
let mut f = Some(f);
#[hdl]
if let HdlSome(v) = expr {
f.take().unwrap()(v);
}
expr
}
#[hdl]
#[track_caller]
pub fn is_none(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let is_none_out: Bool = wire();
connect(is_none_out, false);
#[hdl]
if let HdlNone = expr {
connect(is_none_out, true);
}
is_none_out
}
#[hdl]
#[track_caller]
pub fn is_some(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let is_some_out: Bool = wire();
connect(is_some_out, false);
#[hdl]
if let HdlSome(_) = expr {
connect(is_some_out, true);
}
is_some_out
}
#[hdl]
#[track_caller]
pub fn map_or<R: Type>(
expr: Expr<Self>,
default: Expr<R>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> {
#[hdl]
let mapped = wire(Expr::ty(default));
let mut f = Some(f);
#[hdl]
match expr {
HdlSome(v) => connect(mapped, f.take().unwrap()(v)),
HdlNone => connect(mapped, default),
}
mapped
}
#[hdl]
#[track_caller]
pub fn map_or_else<R: Type>(
expr: Expr<Self>,
default: impl FnOnce() -> Expr<R>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<R> {
#[hdl]
let mut mapped = incomplete_wire();
let mut default = Some(default);
let mut f = Some(f);
let mut retval = None;
#[hdl]
match expr {
HdlSome(v) => {
let v = f.take().unwrap()(v);
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v);
}
HdlNone => {
let v = default.take().unwrap()();
let mapped = *retval.get_or_insert_with(|| mapped.complete(Expr::ty(v)));
connect(mapped, v);
}
}
retval.unwrap()
}
#[hdl]
#[track_caller]
pub fn or(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let or_out = wire(Expr::ty(expr));
connect(or_out, opt_b);
#[hdl]
if let HdlSome(_) = expr {
connect(or_out, expr);
}
or_out
}
#[hdl]
#[track_caller]
pub fn or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<Self>) -> Expr<Self> {
#[hdl]
let or_else_out = wire(Expr::ty(expr));
connect(or_else_out, f());
#[hdl]
if let HdlSome(_) = expr {
connect(or_else_out, expr);
}
or_else_out
}
#[hdl]
#[track_caller]
pub fn unwrap_or(expr: Expr<Self>, default: Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(default));
connect(unwrap_or_else_out, default);
#[hdl]
if let HdlSome(v) = expr {
connect(unwrap_or_else_out, v);
}
unwrap_or_else_out
}
#[hdl]
#[track_caller]
pub fn unwrap_or_else(expr: Expr<Self>, f: impl FnOnce() -> Expr<T>) -> Expr<T> {
#[hdl]
let unwrap_or_else_out = wire(Expr::ty(expr).HdlSome);
connect(unwrap_or_else_out, f());
#[hdl]
if let HdlSome(v) = expr {
connect(unwrap_or_else_out, v);
}
unwrap_or_else_out
}
#[hdl]
#[track_caller]
pub fn xor(expr: Expr<Self>, opt_b: Expr<Self>) -> Expr<Self> {
#[hdl]
let xor_out = wire(Expr::ty(expr));
#[hdl]
if let HdlSome(_) = expr {
#[hdl]
if let HdlNone = opt_b {
connect(xor_out, expr);
} else {
connect(xor_out, Expr::ty(expr).HdlNone());
}
} else {
connect(xor_out, opt_b);
}
xor_out
}
#[hdl]
#[track_caller]
pub fn zip<U: Type>(expr: Expr<Self>, other: Expr<HdlOption<U>>) -> Expr<HdlOption<(T, U)>> {
#[hdl]
let zip_out = wire(HdlOption[(Expr::ty(expr).HdlSome, Expr::ty(other).HdlSome)]);
connect(zip_out, Expr::ty(zip_out).HdlNone());
#[hdl]
if let HdlSome(l) = expr {
#[hdl]
if let HdlSome(r) = other {
connect(zip_out, HdlSome((l, r)));
}
}
zip_out
}
}
impl<T: Type> HdlOption<HdlOption<T>> {
#[hdl]
#[track_caller]
pub fn flatten(expr: Expr<Self>) -> Expr<HdlOption<T>> {
#[hdl]
let flattened = wire(Expr::ty(expr).HdlSome);
#[hdl]
match expr {
HdlSome(v) => connect(flattened, v),
HdlNone => connect(flattened, Expr::ty(expr).HdlSome.HdlNone()),
}
flattened
}
}
impl<T: Type, U: Type> HdlOption<(T, U)> {
#[hdl]
#[track_caller]
pub fn unzip(expr: Expr<Self>) -> Expr<(HdlOption<T>, HdlOption<U>)> {
let (t, u) = Expr::ty(expr).HdlSome;
#[hdl]
let unzipped = wire((HdlOption[t], HdlOption[u]));
connect(unzipped, (HdlOption[t].HdlNone(), HdlOption[u].HdlNone()));
#[hdl]
if let HdlSome(v) = expr {
connect(unzipped.0, HdlSome(v.0));
connect(unzipped.1, HdlSome(v.1));
}
unzipped
}
}

View file

@ -22,7 +22,7 @@ use crate::{
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
util::ScopedRef, util::ScopedRef,
wire::Wire, wire::{IncompleteWire, Wire},
}; };
use hashbrown::{hash_map::Entry, HashMap, HashSet}; use hashbrown::{hash_map::Entry, HashMap, HashSet};
use num_bigint::BigInt; use num_bigint::BigInt;
@ -118,9 +118,35 @@ pub trait BlockRef: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug {}
impl BlockRef for BlockId {} impl BlockRef for BlockId {}
pub(crate) enum IncompleteDeclaration {
Incomplete {
name: ScopedNameId,
source_location: SourceLocation,
},
Complete(StmtDeclaration<ModuleBuilding>),
Taken,
}
impl fmt::Debug for IncompleteDeclaration {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Incomplete {
name,
source_location: _,
} => f
.debug_struct("Incomplete")
.field("name", name)
.finish_non_exhaustive(),
Self::Complete(v) => v.fmt(f),
Self::Taken => f.write_str("Taken"),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct BuilderBlock { pub struct BuilderBlock {
memories: Vec<Rc<RefCell<MemBuilderTarget>>>, memories: Vec<Rc<RefCell<MemBuilderTarget>>>,
incomplete_declarations: Vec<Rc<RefCell<IncompleteDeclaration>>>,
stmts: Vec<Stmt<ModuleBuilding>>, stmts: Vec<Stmt<ModuleBuilding>>,
} }
@ -831,13 +857,34 @@ impl From<NormalModuleBody<ModuleBuilding>> for NormalModuleBody {
annotations_map: &mut HashMap<StmtDeclaration<ModuleBuilding>, Vec<TargetedAnnotation>>, annotations_map: &mut HashMap<StmtDeclaration<ModuleBuilding>, Vec<TargetedAnnotation>>,
block_id: BlockId, block_id: BlockId,
) -> Block { ) -> Block {
let BuilderBlock { memories, stmts } = &mut blocks[block_id.as_usize()]; let BuilderBlock {
memories,
incomplete_declarations,
stmts,
} = &mut blocks[block_id.as_usize()];
let memories = Interned::from_iter( let memories = Interned::from_iter(
memories memories
.drain(..) .drain(..)
.filter_map(|memory| memory.borrow().make_memory()), .filter_map(|memory| memory.borrow().make_memory()),
); );
let stmts = std::mem::take(stmts); let stmts = Vec::from_iter(
incomplete_declarations
.drain(..)
.map(|decl| {
match std::mem::replace(
&mut *decl.borrow_mut(),
IncompleteDeclaration::Taken,
) {
IncompleteDeclaration::Incomplete {
name,
source_location,
} => panic!("incomplete declaration: {name:?}\nat: {source_location}"),
IncompleteDeclaration::Complete(v) => Stmt::Declaration(v),
IncompleteDeclaration::Taken => unreachable!(),
}
})
.chain(stmts.drain(..)),
);
let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| { let stmts = Interned::from_iter(stmts.into_iter().map(|stmt| {
match stmt { match stmt {
Stmt::Connect(stmt) => stmt.into(), Stmt::Connect(stmt) => stmt.into(),
@ -908,6 +955,7 @@ impl NormalModuleBody<ModuleBuilding> {
let index = self.body.blocks.len(); let index = self.body.blocks.len();
self.body.blocks.push(BuilderBlock { self.body.blocks.push(BuilderBlock {
memories: vec![], memories: vec![],
incomplete_declarations: vec![],
stmts: vec![], stmts: vec![],
}); });
BlockId(index) BlockId(index)
@ -1943,6 +1991,7 @@ impl ModuleBuilder {
body: BuilderModuleBody { body: BuilderModuleBody {
blocks: vec![BuilderBlock { blocks: vec![BuilderBlock {
memories: vec![], memories: vec![],
incomplete_declarations: vec![],
stmts: vec![], stmts: vec![],
}], }],
annotations_map: HashMap::new(), annotations_map: HashMap::new(),
@ -2156,6 +2205,42 @@ pub fn wire<T: Type>(implicit_name: ImplicitName<'_>, ty: T) -> Expr<T> {
wire_with_loc(implicit_name.0, SourceLocation::caller(), ty) wire_with_loc(implicit_name.0, SourceLocation::caller(), ty)
} }
#[track_caller]
fn incomplete_declaration(
name: &str,
source_location: SourceLocation,
) -> Rc<RefCell<IncompleteDeclaration>> {
ModuleBuilder::with(|m| {
let mut impl_ = m.impl_.borrow_mut();
let scoped_name = ScopedNameId(m.name, impl_.name_id_gen.gen(name.intern()));
drop(impl_);
let retval = Rc::new(RefCell::new(IncompleteDeclaration::Incomplete {
name: scoped_name,
source_location,
}));
let mut impl_ = m.impl_.borrow_mut();
impl_
.body
.builder_normal_body()
.block(m.block_stack.top())
.incomplete_declarations
.push(retval.clone());
retval
})
}
#[track_caller]
pub fn incomplete_wire_with_loc(name: &str, source_location: SourceLocation) -> IncompleteWire {
IncompleteWire {
declaration: incomplete_declaration(name, source_location),
}
}
#[track_caller]
pub fn incomplete_wire(implicit_name: ImplicitName<'_>) -> IncompleteWire {
incomplete_wire_with_loc(implicit_name.0, SourceLocation::caller())
}
#[track_caller] #[track_caller]
pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> { pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> {
ModuleBuilder::with(|m| { ModuleBuilder::with(|m| {

View file

@ -9,8 +9,8 @@ pub use crate::{
int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType},
memory::{Mem, MemBuilder, ReadUnderWrite}, memory::{Mem, MemBuilder, ReadUnderWrite},
module::{ module::{
annotate, connect, connect_any, instance, memory, memory_array, memory_with_init, annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
reg_builder, wire, Instance, Module, ModuleBuilder, memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
}, },
reg::Reg, reg::Reg,
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},

View file

@ -25,3 +25,5 @@ pub use scoped_ref::ScopedRef;
pub use misc::{ pub use misc::{
interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice, interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
}; };
pub mod ready_valid;

View file

@ -0,0 +1,34 @@
use crate::prelude::*;
#[hdl]
pub struct ReadyValid<T> {
pub data: HdlOption<T>,
#[hdl(flip)]
pub ready: Bool,
}
impl<T: Type> ReadyValid<T> {
#[hdl]
pub fn fire(expr: Expr<Self>) -> Expr<Bool> {
#[hdl]
let fire: Bool = wire();
#[hdl]
match expr.data {
HdlNone => connect(fire, false),
HdlSome(_) => connect(fire, expr.ready),
}
fire
}
#[hdl]
pub fn map<R: Type>(
expr: Expr<Self>,
f: impl FnOnce(Expr<T>) -> Expr<R>,
) -> Expr<ReadyValid<R>> {
let data = HdlOption::map(expr.data, f);
#[hdl]
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
connect(mapped.data, data);
connect(expr.ready, mapped.ready);
mapped
}
}

View file

@ -1,13 +1,13 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
// See Notices.txt for copyright information // See Notices.txt for copyright information
use crate::{ use crate::{
expr::Flow, expr::{Expr, Flow, ToExpr},
intern::Interned, intern::Interned,
module::{NameId, ScopedNameId}, module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
source_location::SourceLocation, source_location::SourceLocation,
ty::{CanonicalType, Type}, ty::{CanonicalType, Type},
}; };
use std::fmt; use std::{cell::RefCell, fmt, rc::Rc};
#[derive(Copy, Clone, Eq, PartialEq, Hash)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Wire<T: Type> { pub struct Wire<T: Type> {
@ -76,3 +76,57 @@ impl<T: Type> Wire<T> {
true true
} }
} }
#[derive(Clone)]
pub struct IncompleteWire {
pub(crate) declaration: Rc<RefCell<IncompleteDeclaration>>,
}
impl IncompleteWire {
#[track_caller]
pub fn complete<T: Type>(&mut self, ty: T) -> Expr<T> {
let canonical_type = ty.canonical();
let mut declaration = self.declaration.borrow_mut();
if let IncompleteDeclaration::Incomplete {
name,
source_location,
} = *declaration
{
*declaration = IncompleteDeclaration::Complete(
StmtWire {
annotations: (),
wire: Wire {
name,
source_location,
ty: canonical_type,
},
}
.into(),
);
}
match *declaration {
IncompleteDeclaration::Complete(StmtDeclaration::Wire(StmtWire {
wire:
Wire {
name,
source_location,
ty: wire_ty,
},
..
})) => {
drop(declaration);
assert_eq!(wire_ty, canonical_type, "type mismatch");
Wire {
name,
source_location,
ty,
}
.to_expr()
}
IncompleteDeclaration::Taken => panic!("can't use wire outside of containing module"),
IncompleteDeclaration::Complete(_) | IncompleteDeclaration::Incomplete { .. } => {
unreachable!()
}
}
}
}

View file

@ -1,4 +1,4 @@
error: top-level #[hdl] can only be used on structs or enums error: top-level #[hdl] can only be used on structs, enums, or functions
--> tests/ui/hdl_types.rs:5:1 --> tests/ui/hdl_types.rs:5:1
| |
5 | #[hdl] 5 | #[hdl]