diff --git a/crates/fayalite-proc-macros-impl/src/lib.rs b/crates/fayalite-proc-macros-impl/src/lib.rs index 3ec00bf..903983f 100644 --- a/crates/fayalite-proc-macros-impl/src/lib.rs +++ b/crates/fayalite-proc-macros-impl/src/lib.rs @@ -65,6 +65,7 @@ mod kw { custom_keyword!(hdl); custom_keyword!(hdl_module); custom_keyword!(input); + custom_keyword!(incomplete_wire); custom_keyword!(instance); custom_keyword!(m); custom_keyword!(memory); diff --git a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs index d4d92b2..1f9565a 100644 --- a/crates/fayalite-proc-macros-impl/src/module/transform_body.rs +++ b/crates/fayalite-proc-macros-impl/src/module/transform_body.rs @@ -34,6 +34,7 @@ options! { Instance(instance), RegBuilder(reg_builder), Wire(wire), + IncompleteWire(incomplete_wire), Memory(memory), MemoryArray(memory_array), 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 for HdlLetKindIncomplete { + fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result { + 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! { pub(crate) enum MemoryFnName { Memory(memory), @@ -697,6 +733,7 @@ impl HdlLetKindMemory { #[derive(Clone, Debug)] pub(crate) enum HdlLetKind { IO(HdlLetKindIO), + Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), Wire(HdlLetKindWire), @@ -706,6 +743,7 @@ pub(crate) enum HdlLetKind { impl_fold! { enum HdlLetKind { IO(HdlLetKindIO), + Incomplete(HdlLetKindIncomplete), Instance(HdlLetKindInstance), RegBuilder(HdlLetKindRegBuilder), Wire(HdlLetKindWire), @@ -720,6 +758,9 @@ impl, I> ParseTypes> for HdlLetKind { ) -> Result { match input { 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) => { ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance) } @@ -871,6 +912,20 @@ impl HdlLetKindParse for HdlLetKind { 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( input, parsed_ty, @@ -903,6 +958,7 @@ impl HdlLetKindToTokens for HdlLetKind { fn ty_to_tokens(&self, tokens: &mut TokenStream) { match self { 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::RegBuilder(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) { match self { 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::RegBuilder(v) => v.expr_to_tokens(tokens), HdlLetKind::Wire(v) => v.expr_to_tokens(tokens), @@ -1369,6 +1426,31 @@ impl Visitor<'_> { semi_token: hdl_let.semi_token, } } + fn process_hdl_let_incomplete(&mut self, hdl_let: HdlLet) -> 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) -> Local { let name = &hdl_let.name; let memory_fn = hdl_let.kind.memory_fn; @@ -1438,6 +1520,7 @@ impl Visitor<'_> { } the_match! { IO => process_hdl_let_io, + Incomplete => process_hdl_let_incomplete, Instance => process_hdl_let_instance, RegBuilder => process_hdl_let_reg_builder, Wire => process_hdl_let_wire, diff --git a/crates/fayalite/src/module.rs b/crates/fayalite/src/module.rs index cb57758..3a17343 100644 --- a/crates/fayalite/src/module.rs +++ b/crates/fayalite/src/module.rs @@ -22,7 +22,7 @@ use crate::{ source_location::SourceLocation, ty::{CanonicalType, Type}, util::ScopedRef, - wire::Wire, + wire::{IncompleteWire, Wire}, }; use hashbrown::{hash_map::Entry, HashMap, HashSet}; use num_bigint::BigInt; @@ -118,9 +118,35 @@ pub trait BlockRef: 'static + Send + Sync + Copy + Eq + Hash + fmt::Debug {} impl BlockRef for BlockId {} +pub(crate) enum IncompleteDeclaration { + Incomplete { + name: ScopedNameId, + source_location: SourceLocation, + }, + Complete(StmtDeclaration), + 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)] pub struct BuilderBlock { memories: Vec>>, + incomplete_declarations: Vec>>, stmts: Vec>, } @@ -831,13 +857,34 @@ impl From> for NormalModuleBody { annotations_map: &mut HashMap, Vec>, block_id: BlockId, ) -> 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( memories .drain(..) .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| { match stmt { Stmt::Connect(stmt) => stmt.into(), @@ -908,6 +955,7 @@ impl NormalModuleBody { let index = self.body.blocks.len(); self.body.blocks.push(BuilderBlock { memories: vec![], + incomplete_declarations: vec![], stmts: vec![], }); BlockId(index) @@ -1943,6 +1991,7 @@ impl ModuleBuilder { body: BuilderModuleBody { blocks: vec![BuilderBlock { memories: vec![], + incomplete_declarations: vec![], stmts: vec![], }], annotations_map: HashMap::new(), @@ -2156,6 +2205,42 @@ pub fn wire(implicit_name: ImplicitName<'_>, ty: T) -> Expr { wire_with_loc(implicit_name.0, SourceLocation::caller(), ty) } +#[track_caller] +fn incomplete_declaration( + name: &str, + source_location: SourceLocation, +) -> Rc> { + 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] pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> { ModuleBuilder::with(|m| { diff --git a/crates/fayalite/src/prelude.rs b/crates/fayalite/src/prelude.rs index 14c3aa7..bedece2 100644 --- a/crates/fayalite/src/prelude.rs +++ b/crates/fayalite/src/prelude.rs @@ -9,8 +9,8 @@ pub use crate::{ int::{Bool, DynSize, IntCmp, KnownSize, SInt, SIntType, Size, UInt, UIntType}, memory::{Mem, MemBuilder, ReadUnderWrite}, module::{ - annotate, connect, connect_any, instance, memory, memory_array, memory_with_init, - reg_builder, wire, Instance, Module, ModuleBuilder, + annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array, + memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder, }, reg::Reg, reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset}, diff --git a/crates/fayalite/src/wire.rs b/crates/fayalite/src/wire.rs index b84d6ae..85ab342 100644 --- a/crates/fayalite/src/wire.rs +++ b/crates/fayalite/src/wire.rs @@ -1,13 +1,13 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // See Notices.txt for copyright information use crate::{ - expr::Flow, + expr::{Expr, Flow, ToExpr}, intern::Interned, - module::{NameId, ScopedNameId}, + module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire}, source_location::SourceLocation, ty::{CanonicalType, Type}, }; -use std::fmt; +use std::{cell::RefCell, fmt, rc::Rc}; #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub struct Wire { @@ -76,3 +76,57 @@ impl Wire { true } } + +#[derive(Clone)] +pub struct IncompleteWire { + pub(crate) declaration: Rc>, +} + +impl IncompleteWire { + #[track_caller] + pub fn complete(&mut self, ty: T) -> Expr { + 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!() + } + } + } +}