add support for incomplete_wire -- a wire that you can supply the type of later
This commit is contained in:
		
							parent
							
								
									ff94dda922
								
							
						
					
					
						commit
						df55a514e4
					
				
					 5 changed files with 231 additions and 8 deletions
				
			
		|  | @ -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); | ||||
|  |  | |||
|  | @ -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<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! { | ||||
|     pub(crate) enum MemoryFnName { | ||||
|         Memory(memory), | ||||
|  | @ -697,6 +733,7 @@ impl HdlLetKindMemory { | |||
| #[derive(Clone, Debug)] | ||||
| pub(crate) enum HdlLetKind<IOType = ParsedType> { | ||||
|     IO(HdlLetKindIO<ModuleIOKind, IOType>), | ||||
|     Incomplete(HdlLetKindIncomplete), | ||||
|     Instance(HdlLetKindInstance), | ||||
|     RegBuilder(HdlLetKindRegBuilder), | ||||
|     Wire(HdlLetKindWire), | ||||
|  | @ -706,6 +743,7 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> { | |||
| impl_fold! { | ||||
|     enum HdlLetKind<IOType,> { | ||||
|         IO(HdlLetKindIO<ModuleIOKind, IOType>), | ||||
|         Incomplete(HdlLetKindIncomplete), | ||||
|         Instance(HdlLetKindInstance), | ||||
|         RegBuilder(HdlLetKindRegBuilder), | ||||
|         Wire(HdlLetKindWire), | ||||
|  | @ -720,6 +758,9 @@ impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> { | |||
|     ) -> Result<Self, ParseFailed> { | ||||
|         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<Type> { | |||
|                     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<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 { | ||||
|         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, | ||||
|  |  | |||
|  | @ -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<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)] | ||||
| pub struct BuilderBlock { | ||||
|     memories: Vec<Rc<RefCell<MemBuilderTarget>>>, | ||||
|     incomplete_declarations: Vec<Rc<RefCell<IncompleteDeclaration>>>, | ||||
|     stmts: Vec<Stmt<ModuleBuilding>>, | ||||
| } | ||||
| 
 | ||||
|  | @ -831,13 +857,34 @@ impl From<NormalModuleBody<ModuleBuilding>> for NormalModuleBody { | |||
|             annotations_map: &mut HashMap<StmtDeclaration<ModuleBuilding>, Vec<TargetedAnnotation>>, | ||||
|             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<ModuleBuilding> { | |||
|         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<T: Type>(implicit_name: ImplicitName<'_>, ty: T) -> Expr<T> { | |||
|     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] | ||||
| pub fn reg_builder_with_loc(name: &str, source_location: SourceLocation) -> RegBuilder<(), (), ()> { | ||||
|     ModuleBuilder::with(|m| { | ||||
|  |  | |||
|  | @ -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}, | ||||
|  |  | |||
|  | @ -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<T: Type> { | ||||
|  | @ -76,3 +76,57 @@ impl<T: Type> Wire<T> { | |||
|         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!() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue