add support for incomplete_wire -- a wire that you can supply the type of later

This commit is contained in:
Jacob Lifshay 2024-09-20 18:46:56 -07:00
parent ff94dda922
commit df55a514e4
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
5 changed files with 231 additions and 8 deletions

View file

@ -65,6 +65,7 @@ mod kw {
custom_keyword!(hdl); custom_keyword!(hdl);
custom_keyword!(hdl_module); 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);

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),
@ -1369,6 +1426,31 @@ 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;
@ -1438,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,

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

@ -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!()
}
}
}
}