From 7bd9167fb047e4b7cf469925677e3e9bb15eaf3f Mon Sep 17 00:00:00 2001 From: Lennart Van Hirtum Date: Tue, 19 Dec 2023 12:35:13 +0100 Subject: [PATCH] Work converting all UUID::INVALID to Option --- src/ast.rs | 2 +- src/codegen_fallback.rs | 20 +++--- src/dev_aid/syntax_highlighting.rs | 4 +- src/flattening.rs | 99 +++++++++++++++------------- src/instantiation/latency.rs | 7 +- src/instantiation/mod.rs | 73 ++++++++++++--------- src/linker.rs | 100 +++++++++++++++-------------- src/main.rs | 12 +++- src/parser.rs | 2 +- src/typing.rs | 17 +++-- 10 files changed, 187 insertions(+), 149 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index f87eb2e..bc22b39 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -176,7 +176,7 @@ impl Module { } #[derive(Debug,Clone,Copy)] -pub struct GlobalReference(pub Span, pub NamedUUID); // token index, and name span +pub struct GlobalReference(pub Span, pub Option); // token index, and name span #[derive(Debug)] pub struct ASTRoot { diff --git a/src/codegen_fallback.rs b/src/codegen_fallback.rs index 2e13337..ee29878 100644 --- a/src/codegen_fallback.rs +++ b/src/codegen_fallback.rs @@ -42,10 +42,11 @@ pub fn value_to_str(value : &Value) -> String { } } -pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option { +pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> String { + assert!(!instance.errors.did_error(), "Module cannot have experienced an error"); let mut program_text : String = format!("module {}(\n\tinput clk, \n", md.link_info.name); - for (port, real_port) in zip(&md.interface.interface_wires, &instance.interface) { - if real_port.id == UUID::INVALID {return None;} + let submodule_interface = instance.interface.as_ref().unwrap(); + for (port, real_port) in zip(&md.interface.interface_wires, submodule_interface) { let wire = &instance.wires[real_port.id]; program_text.push_str(if port.is_input {"\tinput"} else {"\toutput /*mux_wire*/ reg"}); program_text.push_str(&typ_to_verilog_array(&wire.typ)); @@ -64,7 +65,7 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option< } } let wire_or_reg = if let RealWireDataSource::Multiplexer{is_state: initial_value, sources: _} = &w.source { - if let StateInitialValue::NotState = initial_value { + if let StateInitialValue::Combinatorial = initial_value { "/*mux_wire*/ reg" } else { "reg" @@ -100,7 +101,8 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option< program_text.push(' '); program_text.push_str(&sm.name); program_text.push_str("(\n.clk(clk)"); - for (port, wire) in zip(&sm.instance.interface, &sm.wires) { + let Some(sm_interface) = &sm.instance.interface else {unreachable!()}; // Having an invalid interface in a submodule is an error! This should have been caught before! + for (port, wire) in zip(sm_interface, &sm.wires) { program_text.push_str(",\n."); program_text.push_str(&sm.instance.wires[port.id].name); program_text.push('('); @@ -116,7 +118,7 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option< RealWireDataSource::Multiplexer { is_state, sources } => { let output_name = w.name.deref(); match is_state { - StateInitialValue::NotState => { + StateInitialValue::Combinatorial => { program_text.push_str(&format!("/*always_comb*/ always @(*) begin\n\t{output_name} <= 1'bX; // Not defined when not valid\n")); } StateInitialValue::State{initial_value : _} => { @@ -135,8 +137,8 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option< } } let from_name = instance.wires[s.from.from].name.deref(); - if s.from.condition != UUID::INVALID { - let cond = instance.wires[s.from.condition].name.deref(); + if let Some(cond) = s.from.condition { + let cond = instance.wires[cond].name.deref(); program_text.push_str(&format!("\tif({cond}) begin {output_name}{path} <= {from_name}; end\n")); } else { program_text.push_str(&format!("\t{output_name}{path} <= {from_name};\n")); @@ -153,5 +155,5 @@ pub fn gen_verilog_code(md : &Module, instance : &InstantiatedModule) -> Option< program_text.push_str("endmodule\n"); - Some(program_text) + program_text } diff --git a/src/dev_aid/syntax_highlighting.rs b/src/dev_aid/syntax_highlighting.rs index a34e774..fa329dd 100644 --- a/src/dev_aid/syntax_highlighting.rs +++ b/src/dev_aid/syntax_highlighting.rs @@ -134,8 +134,8 @@ fn walk_name_color(all_objects : &[NamedUUID], links : &Links, result : &mut [ID result[name_part].typ = IDETokenType::Identifier(ide_typ); } for GlobalReference(reference_span, ref_uuid) in &link_info.global_references { - let typ = if *ref_uuid != NamedUUID::INVALID { - IDETokenType::Identifier(links.globals[*ref_uuid].get_ide_type()) + let typ = if let Some(id) = ref_uuid { + IDETokenType::Identifier(links.globals[*id].get_ide_type()) } else { IDETokenType::Invalid }; diff --git a/src/flattening.rs b/src/flattening.rs index 1f3edd5..9642621 100644 --- a/src/flattening.rs +++ b/src/flattening.rs @@ -3,7 +3,7 @@ use std::{ops::{Deref, Range}, iter::zip}; use crate::{ ast::{Span, Value, Module, Expression, SpanExpression, LocalOrGlobal, Operator, AssignableExpression, SpanAssignableExpression, Statement, CodeBlock, IdentifierType, GlobalReference, TypeExpression, DeclIDMarker, DeclID}, linker::{Linker, Named, Linkable, get_builtin_uuid, FileUUID, NamedUUID}, - errors::{ErrorCollector, error_info}, arena_alloc::{ListAllocator, UUID, UUIDMarker, FlatAlloc}, tokenizer::kw, typing::{Type, typecheck_unary_operator, get_binary_operator_types, typecheck, typecheck_is_array_indexer} + errors::{ErrorCollector, error_info}, arena_alloc::{ListAllocator, UUID, UUIDMarker, FlatAlloc}, tokenizer::kw, typing::{Type, typecheck_unary_operator, get_binary_operator_types, typecheck, typecheck_is_array_indexer, SpanType} }; #[derive(Debug,Clone,Copy,PartialEq,Eq,Hash)] @@ -35,6 +35,14 @@ impl ConnectionWrite { } } +#[derive(Debug)] +pub struct Connection { + pub num_regs : i64, + pub from : SpanFlatID, + pub to : ConnectionWrite, + pub condition : Option +} + #[derive(Debug,Clone,Copy)] pub struct InterfacePort { pub is_input : bool, @@ -53,6 +61,7 @@ pub enum WireSource { #[derive(Debug)] pub struct WireInstance { pub typ : Type, + pub span : Span, pub inst : WireSource } @@ -71,14 +80,6 @@ impl Instantiation { } } -#[derive(Debug)] -pub struct Connection { - pub num_regs : i64, - pub from : SpanFlatID, - pub to : ConnectionWrite, - pub condition : FlatID -} - struct FlatteningContext<'l, 'm, 'fl> { decl_to_flat_map : FlatAlloc, instantiations : &'fl ListAllocator, @@ -93,15 +94,24 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let found = &self.instantiations[wire.0].extract_wire().typ; typecheck(found, wire.1, expected, context, self.linker, &self.errors) } - pub fn map_to_type(&self, type_expr : &TypeExpression, global_references : &[GlobalReference]) -> Option { + pub fn map_to_type(&self, type_expr : &TypeExpression, global_references : &[GlobalReference]) -> Type { match type_expr { - TypeExpression::Named(n) => Some(Type::Named(global_references[*n].1)), + TypeExpression::Named(n) => { + if let Some(found_ref) = global_references[*n].1 { + Type::Named(found_ref) + } else { + Type::Invalid + } + } TypeExpression::Array(b) => { let (array_type_expr, array_size_expr) = b.deref(); - let array_element_type = self.map_to_type(array_type_expr, global_references)?; - let array_size_wire = self.flatten_single_expr(array_size_expr, FlatID::INVALID)?; - Some(Type::Array(Box::new((array_element_type, array_size_wire.0)))) - }, + let array_element_type = self.map_to_type(array_type_expr, global_references); + if let Some(array_size_wire) = self.flatten_single_expr(array_size_expr, None) { + Type::Array(Box::new((array_element_type, array_size_wire.0))) + } else { + Type::Invalid + } + } } } // May also error, for example when array accesses happen on non-array types @@ -134,12 +144,12 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } else { IdentifierType::Output }; - InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::Wire(WireInstance{typ: port.typ.clone(), inst : WireSource::NamedWire { read_only : !port.is_input, identifier_type, decl_id : None }}))} + InterfacePort{is_input : port.is_input, id : self.instantiations.alloc(Instantiation::Wire(WireInstance{typ: port.typ.clone(), span : typ_span, inst : WireSource::NamedWire { read_only : !port.is_input, identifier_type, decl_id : None }}))} }).collect(); Instantiation::SubModule{name, module_uuid, typ_span, interface_wires} } - fn desugar_func_call(&self, func_and_args : &[SpanExpression], closing_bracket_pos : usize, condition : FlatID) -> Option<(&Module, &[InterfacePort])> { + fn desugar_func_call(&self, func_and_args : &[SpanExpression], closing_bracket_pos : usize, condition : Option) -> Option<(&Module, &[InterfacePort])> { let (name_expr, name_expr_span) = &func_and_args[0]; // Function name is always there let func_instantiation_id = match name_expr { Expression::Named(LocalOrGlobal::Local(l)) => { @@ -149,7 +159,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let module_ref = self.module.link_info.global_references[*g]; let dependency = self.linker.try_get_module(module_ref, &self.errors)?; - let new_module_interface = self.alloc_module_interface(dependency.link_info.name.clone(), dependency, module_ref.1, *name_expr_span); + let new_module_interface = self.alloc_module_interface(dependency.link_info.name.clone(), dependency, module_ref.1?, *name_expr_span); self.instantiations.alloc(new_module_interface) } _other => { @@ -192,7 +202,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Some((md, &interface_wires[output_range])) } - fn flatten_single_expr(&self, (expr, expr_span) : &SpanExpression, condition : FlatID) -> Option { + fn flatten_single_expr(&self, (expr, expr_span) : &SpanExpression, condition : Option) -> Option { let single_connection_side = match expr { Expression::Named(LocalOrGlobal::Local(l)) => { assert!(self.decl_to_flat_map[*l] != UUID::INVALID); @@ -201,17 +211,17 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Expression::Named(LocalOrGlobal::Global(g)) => { let r = self.module.link_info.global_references[*g]; let cst = self.linker.try_get_constant(r, &self.errors)?; - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), inst : WireSource::Constant{value : cst}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), span : *expr_span, inst : WireSource::Constant{value : cst}})) } Expression::Constant(cst) => { - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), inst : WireSource::Constant{value : cst.clone()}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : cst.get_type(), span : *expr_span, inst : WireSource::Constant{value : cst.clone()}})) } Expression::UnaryOp(op_box) => { let (op, _op_pos, operate_on) = op_box.deref(); let right = self.flatten_single_expr(operate_on, condition)?; let found = &self.instantiations[right.0].extract_wire().typ; let output_type = typecheck_unary_operator(*op, found, right.1, self.linker, &self.errors); - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, inst : WireSource::UnaryOp{op : *op, right}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, span : *expr_span, inst : WireSource::UnaryOp{op : *op, right}})) } Expression::BinOp(binop_box) => { let (left_expr, op, _op_pos, right_expr) = binop_box.deref(); @@ -220,7 +230,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let ((input_left_type, input_right_type), output_type) = get_binary_operator_types(*op); self.typecheck(left, &input_left_type, &format!("{op} left"))?; self.typecheck(right, &input_right_type, &format!("{op} right"))?; - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, inst : WireSource::BinaryOp{op : *op, left, right}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : output_type, span : *expr_span, inst : WireSource::BinaryOp{op : *op, left, right}})) } Expression::Array(arr_box) => { let (left, right, _bracket_span) = arr_box.deref(); @@ -231,7 +241,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let array_type = &self.instantiations[arr.0].extract_wire().typ; let typ = typecheck_is_array_indexer(array_type, arr.1, self.linker, &self.errors)?.clone(); index_was_int?; // Do both for better typechecking diagnostics - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ, inst : WireSource::ArrayAccess{arr, arr_idx}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ, span : *expr_span, inst : WireSource::ArrayAccess{arr, arr_idx}})) } Expression::FuncCall(func_and_args) => { let (md, outputs) = self.desugar_func_call(func_and_args, expr_span.1, condition)?; @@ -247,7 +257,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { }; Some((single_connection_side, *expr_span)) } - fn flatten_assignable_expr(&self, (expr, span) : &SpanAssignableExpression, condition : FlatID) -> Option { + fn flatten_assignable_expr(&self, (expr, span) : &SpanAssignableExpression, condition : Option) -> Option { Some(match expr { AssignableExpression::Named{local_idx} => { let root = self.decl_to_flat_map[*local_idx]; @@ -275,22 +285,22 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { } }) } - fn extend_condition(&self, condition : FlatID, additional_condition : SpanFlatID) -> FlatID { - if condition == FlatID::INVALID { - additional_condition.0 - } else { + fn extend_condition(&self, condition : Option, additional_condition : SpanFlatID) -> FlatID { + if let Some(condition) = condition { let bool_typ = Type::Named(get_builtin_uuid("bool")); assert!(self.instantiations[condition].extract_wire().typ == bool_typ); - self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, inst : WireSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : (condition, additional_condition.1), right : additional_condition}})) + self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, span : additional_condition.1, inst : WireSource::BinaryOp{op: Operator{op_typ : kw("&")}, left : (condition, additional_condition.1), right : additional_condition}})) + } else { + additional_condition.0 } } - fn flatten_code(&mut self, code : &CodeBlock, condition : FlatID) { + fn flatten_code(&mut self, code : &CodeBlock, condition : Option) { for (stmt, stmt_span) in &code.statements { match stmt { Statement::Declaration(decl_id) => { let decl = &self.module.declarations[*decl_id]; - let Some(typ) = self.map_to_type(&decl.typ.0, &self.module.link_info.global_references) else {continue;}; + let typ = self.map_to_type(&decl.typ.0, &self.module.link_info.global_references) else {continue;}; let typ_span = decl.typ.1; let decl_typ_root_reference = typ.get_root(); @@ -313,7 +323,7 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { Named::Type(_) => { assert!(decl.identifier_type != IdentifierType::Input); assert!(decl.identifier_type != IdentifierType::Output); - Instantiation::Wire(WireInstance{typ, inst : WireSource::NamedWire{read_only : false, identifier_type : decl.identifier_type, decl_id : Some(*decl_id)}}) + Instantiation::Wire(WireInstance{typ, span : typ_span, inst : WireSource::NamedWire{read_only : false, identifier_type : decl.identifier_type, decl_id : Some(*decl_id)}}) } } }; @@ -326,11 +336,11 @@ impl<'l, 'm, 'fl> FlatteningContext<'l, 'm, 'fl> { let bool_typ = Type::Named(get_builtin_uuid("bool")); if self.typecheck(if_statement_condition, &bool_typ, "if statement condition") == None {continue;} let then_condition = self.extend_condition(condition, if_statement_condition); - self.flatten_code(then, then_condition); + self.flatten_code(then, Some(then_condition)); if let Some(e) = els { - let else_condition_bool = (self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, inst : WireSource::UnaryOp{op : Operator{op_typ : kw("!")}, right : if_statement_condition}})), condition_expr.1); + let else_condition_bool = (self.instantiations.alloc(Instantiation::Wire(WireInstance{typ : bool_typ, span : condition_expr.1, inst : WireSource::UnaryOp{op : Operator{op_typ : kw("!")}, right : if_statement_condition}})), condition_expr.1); let else_condition = self.extend_condition(condition, else_condition_bool); - self.flatten_code(e, else_condition); + self.flatten_code(e, Some(else_condition)); } } Statement::Assign{to, expr : (Expression::FuncCall(func_and_args), func_span), eq_sign_position} => { @@ -457,12 +467,9 @@ impl FlattenedModule { IdentifierType::Local | IdentifierType::State => continue }; - let (wire_id, typ) = if let Some(typ) = context.map_to_type(&decl.typ.0, &module.link_info.global_references) { - let wire_id = context.instantiations.alloc(Instantiation::Wire(WireInstance{typ : typ.clone(), inst : WireSource::NamedWire{read_only: is_input, identifier_type : decl.identifier_type, decl_id : Some(decl_id)}})); - (wire_id, typ) - } else { - (UUID::INVALID, Type::Named(UUID::INVALID)) - }; + let typ = context.map_to_type(&decl.typ.0, &module.link_info.global_references); + let wire_id = context.instantiations.alloc(Instantiation::Wire(WireInstance{typ : typ.clone(), span : decl.typ.1, inst : WireSource::NamedWire{read_only: is_input, identifier_type : decl.identifier_type, decl_id : Some(decl_id)}})); + interface.interface_wires.push(FlattenedInterfacePort { wire_id, is_input, typ, port_name: decl.name.clone(), span: decl.span }); context.decl_to_flat_map[decl_id] = wire_id; } @@ -485,7 +492,7 @@ impl FlattenedModule { module, linker, }; - context.flatten_code(&module.code, FlatID::INVALID); + context.flatten_code(&module.code, None); } pub fn find_unused_variables(&self, md : &Module) { @@ -495,8 +502,8 @@ impl FlattenedModule { for (_id, conn) in &self.instantiations { if let Instantiation::Connection(conn) = conn { connection_fanin[conn.to.root].push(conn.from.0); - if conn.condition != UUID::INVALID { - connection_fanin[conn.to.root].push(conn.condition); + if let Some(cond) = conn.condition { + connection_fanin[conn.to.root].push(cond); } } } @@ -555,7 +562,7 @@ impl FlattenedModule { // Now produce warnings from the unused list for (id, inst) in &self.instantiations { if !is_instance_used_map[id] { - if let Instantiation::Wire(WireInstance{typ : _, inst : WireSource::NamedWire { read_only : _, identifier_type : _, decl_id : Some(decl_id) }}) = inst { + if let Instantiation::Wire(WireInstance{typ : _, span : _, inst : WireSource::NamedWire { read_only : _, identifier_type : _, decl_id : Some(decl_id) }}) = inst { self.errors.warn_basic(Span::from(md.declarations[*decl_id].name_token), "Unused Variable: This variable does not affect the output ports of this module"); } } diff --git a/src/instantiation/latency.rs b/src/instantiation/latency.rs index b957d52..7336c1e 100644 --- a/src/instantiation/latency.rs +++ b/src/instantiation/latency.rs @@ -37,9 +37,12 @@ impl LatencyComputer { // Submodules Fanin for (_id, sub_mod) in submodules { - for (input_wire, input_port) in zip(&sub_mod.wires, &sub_mod.instance.interface) { + // All submodules must be fully valid + assert!(!sub_mod.instance.errors.did_error()); + let sub_mod_interface = sub_mod.instance.interface.as_deref().unwrap(); + for (input_wire, input_port) in zip(&sub_mod.wires, sub_mod_interface) { if !input_port.is_input {continue;} - for (output_wire, output_port) in zip(&sub_mod.wires, &sub_mod.instance.interface) { + for (output_wire, output_port) in zip(&sub_mod.wires, sub_mod_interface) { if output_port.is_input {continue;} let delta_latency = output_port.absolute_latency - input_port.absolute_latency; diff --git a/src/instantiation/mod.rs b/src/instantiation/mod.rs index e0799e8..b9d4b02 100644 --- a/src/instantiation/mod.rs +++ b/src/instantiation/mod.rs @@ -2,7 +2,7 @@ use std::{rc::Rc, ops::Deref, cell::RefCell}; use num::{BigUint, FromPrimitive}; -use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module, IdentifierType}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement, WireSource}, errors::ErrorCollector, linker::{Linker, get_builtin_uuid}}; +use crate::{arena_alloc::{UUID, UUIDMarker, FlatAlloc}, ast::{Value, Operator, Module, IdentifierType}, typing::{ConcreteType, Type}, flattening::{FlatID, Instantiation, FlatIDMarker, ConnectionWrite, ConnectionWritePathElement, WireSource}, errors::{ErrorCollector, error_info}, linker::{Linker, get_builtin_uuid}}; pub mod latency; @@ -20,7 +20,7 @@ pub type SubModuleID = UUID; pub struct ConnectFrom { pub num_regs : i64, pub from : WireID, - pub condition : WireID + pub condition : Option } #[derive(Debug)] @@ -36,7 +36,7 @@ pub struct MultiplexerSource { #[derive(Debug)] pub enum StateInitialValue { - NotState, + Combinatorial, State{initial_value : Option} } @@ -57,7 +57,9 @@ impl RealWireDataSource { RealWireDataSource::Multiplexer { is_state, sources } => { for s in sources { f(s.from.from, s.from.num_regs); - f(s.from.condition, 0); + if let Some(c) = s.from.condition { + f(c, 0); + } } } RealWireDataSource::UnaryOp { op, right } => { @@ -103,7 +105,7 @@ pub struct SubModule { #[derive(Debug)] pub struct InstantiatedModule { pub name : Box, // Unique name involving all template arguments - pub interface : Vec, + pub interface : Option>, // Interface is only valid if all wires of the interface were valid pub wires : FlatAlloc, pub submodules : FlatAlloc, pub errors : ErrorCollector, @@ -146,17 +148,20 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { Value::Integer(BigUint::from_u64(3333333).unwrap()) } } - fn concretize_type(&self, typ : &Type) -> ConcreteType { + fn concretize_type(&self, typ : &Type) -> Option { match typ { + Type::Invalid => { + None + } Type::Named(n) => { - ConcreteType::Named(*n) + Some(ConcreteType::Named(*n)) } Type::Array(arr_box) => { let (arr_content_typ, arr_size_wire) = arr_box.deref(); - let inner_typ = self.concretize_type(arr_content_typ); + let inner_typ = self.concretize_type(arr_content_typ)?; let Value::Integer(v) = self.compute_constant(*arr_size_wire) else {panic!("Not an int, should have been solved beforehand!")}; let arr_usize = u64::try_from(v).expect("Array size cannot exceed u64::MAX"); - ConcreteType::Array(Box::new((inner_typ, arr_usize))) + Some(ConcreteType::Array(Box::new((inner_typ, arr_usize)))) } } } @@ -185,7 +190,7 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { todo!(); } - let RealWire{name : _, latency : _, typ : _, original_wire: _, source : RealWireDataSource::Multiplexer { is_state: initial_value, sources }} = &mut self.wires[self.instance_map[to.root].extract_wire()] else {unreachable!("Should only be a writeable wire here")}; + let RealWire{name : _, latency : _, typ : _, original_wire: _, source : RealWireDataSource::Multiplexer { is_state : _, sources }} = &mut self.wires[self.instance_map[to.root].extract_wire()] else {unreachable!("Should only be a writeable wire here")}; sources.push(MultiplexerSource{from, path : new_path}) } @@ -206,8 +211,8 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { RealWireDataSource::ReadOnly } else { // TODO initial value - let is_state = if *identifier_type == IdentifierType::State {StateInitialValue::State { initial_value: None }} else {StateInitialValue::NotState}; - RealWireDataSource::Multiplexer {is_state, sources : Vec::new()} + let is_state = if *identifier_type == IdentifierType::State{StateInitialValue::State{initial_value: None}} else {StateInitialValue::Combinatorial}; + RealWireDataSource::Multiplexer{is_state, sources : Vec::new()} }; (decl_id.map(|id| self.module.declarations[id].name.clone()), source) } @@ -225,14 +230,15 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { } }; let name = name.unwrap_or_else(|| {format!("_{}", self.wires.get_next_alloc_id().get_hidden_value()).into_boxed_str()}); - SubModuleOrWire::Wire(self.wires.alloc(RealWire{ name, latency : i64::MIN /* Invalid */, typ : self.concretize_type(&w.typ), original_wire, source})) + let Some(typ) = self.concretize_type(&w.typ) else { + let typ_name = w.typ.to_string(self.linker); + self.errors.error_basic(w.span, format!("Could not properly instantiate type {typ_name}")); + return; // Exit early, do not produce invalid wires in InstantiatedModule + }; + SubModuleOrWire::Wire(self.wires.alloc(RealWire{ name, latency : i64::MIN /* Invalid */, typ, original_wire, source})) } Instantiation::Connection(conn) => { - let condition = if conn.condition != UUID::INVALID { - self.instance_map[conn.condition].extract_wire() - } else { - UUID::INVALID - }; + let condition = conn.condition.map(|found_conn| self.instance_map[found_conn].extract_wire()); let conn_from = ConnectFrom { num_regs: conn.num_regs, from: self.instance_map[conn.from.0].extract_wire(), // TODO Span? @@ -248,19 +254,26 @@ impl<'fl, 'l> InstantiationContext<'fl, 'l> { } } - fn make_interface(&self) -> Vec { - self.module.interface.interface_wires.iter().map(|port| { - let real_interface_wire = if port.wire_id != UUID::INVALID { - self.instance_map[port.wire_id].extract_wire() - } else { - UUID::INVALID - }; - InstantiatedInterfacePort { - id: real_interface_wire, - is_input: port.is_input, - absolute_latency: i64::MIN, // INVALID + // Returns a proper interface if all wires involved did not produce an error. If a wire did produce an error then returns None. + fn make_interface(&self) -> Option> { + let mut result = Vec::new(); + result.reserve(self.module.interface.interface_wires.len()); + for port in &self.module.interface.interface_wires { + match &self.instance_map[port.wire_id] { + SubModuleOrWire::Wire(w) => { + result.push(InstantiatedInterfacePort { + id: *w, + is_input: port.is_input, + absolute_latency: i64::MIN, // INVALID + }); + } + SubModuleOrWire::Unnasigned => { + return None + } + SubModuleOrWire::SubModule(_) => unreachable!() } - }).collect() + } + Some(result) } } diff --git a/src/linker.rs b/src/linker.rs index 48d01f6..5b10d21 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -214,14 +214,14 @@ impl Links { fn resolve_dependencies(namespace : &HashMap, NamedUUID>, file : &FileData, link_info : &mut LinkInfo) { let mut all_resolved = true; for GlobalReference(reference_span, uuid) in &mut link_info.global_references { - if *uuid == NamedUUID::INVALID { + if uuid.is_none() { let reference_name_str = file.get_span_text(*reference_span); *uuid = if let Some(found) = namespace.get(reference_name_str) { - *found + Some(*found) } else { all_resolved = false; - UUID::INVALID + None } } } @@ -331,7 +331,7 @@ impl Linker { continue; // Early exit because we know this object contains no linking errors } for GlobalReference(reference_span, ref_uuid) in &object_link_info.global_references { - if *ref_uuid == NamedUUID::INVALID { + if ref_uuid.is_none() { let reference_text = file.get_span_text(*reference_span); errors.error_basic(*reference_span, format!("No Value or Type of the name '{reference_text}' was found. Did you forget to import it?")); } @@ -369,9 +369,11 @@ impl Linker { for (_uuid, v) in &mut self.links.globals { if let Some(info) = v.get_link_info_mut() { // Builtins can't refer to other things for GlobalReference(_name, v) in &mut info.global_references { - if back_reference_set.contains(v) { - *v = NamedUUID::INVALID; - info.is_fully_linked = false; + if let Some(v_id) = *v { + if back_reference_set.contains(&v_id) { + *v = None; + info.is_fully_linked = false; + } } } } @@ -461,49 +463,51 @@ impl Linker { md } - pub fn try_get_constant(&self, GlobalReference(identifier_span, uuid) : GlobalReference, errors : &ErrorCollector) -> Option { - if uuid == UUID::INVALID { - return None; // Error reporting already handled by linking - } - match &self.links.globals[uuid] { - Named::Constant(NamedConstant::Builtin(_name, v)) => { - Some(v.clone()) - }, - other => { - let info = other.get_linking_error_location(); - let infos = if let Some((file, span)) = info.location { - vec![error_info(span, file, "Defined here")] - } else { - vec![] - }; - let name = info.name; - let ident_type = info.named_type; - errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Constant!"), infos); - None + pub fn try_get_constant(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option { + if let Some(uuid) = ref_uuid { + match &self.links.globals[uuid] { + Named::Constant(NamedConstant::Builtin(_name, v)) => { + Some(v.clone()) + }, + other => { + let info = other.get_linking_error_location(); + let infos = if let Some((file, span)) = info.location { + vec![error_info(span, file, "Defined here")] + } else { + vec![] + }; + let name = info.name; + let ident_type = info.named_type; + errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Constant!"), infos); + None + } } - } - } - - pub fn try_get_module(&self, GlobalReference(identifier_span, uuid) : GlobalReference, errors : &ErrorCollector) -> Option<&Module> { - if uuid == UUID::INVALID { - return None; // Error reporting already handled by linking - } - match &self.links.globals[uuid] { - Named::Module(md) => { - Some(md) - }, - other => { - let info = other.get_linking_error_location(); - let infos = if let Some((file, span)) = info.location { - vec![error_info(span, file, "Defined here")] - } else { - vec![] - }; - let name = info.name; - let ident_type = info.named_type; - errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Module!"), infos); - None + } else { + None + } + } + + pub fn try_get_module(&self, GlobalReference(identifier_span, ref_uuid) : GlobalReference, errors : &ErrorCollector) -> Option<&Module> { + if let Some(uuid) = ref_uuid { + match &self.links.globals[uuid] { + Named::Module(md) => { + Some(md) + }, + other => { + let info = other.get_linking_error_location(); + let infos = if let Some((file, span)) = info.location { + vec![error_info(span, file, "Defined here")] + } else { + vec![] + }; + let name = info.name; + let ident_type = info.named_type; + errors.error_with_info(identifier_span, format!("{ident_type} {name} is not a Module!"), infos); + None + } } + } else { + None } } diff --git a/src/main.rs b/src/main.rs index abdb104..0efd1f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,16 +28,22 @@ use codegen_fallback::gen_verilog_code; use dev_aid::syntax_highlighting::*; use linker::{Named, Linker, NamedUUID}; -fn codegen_to_file(linker : &Linker, id : NamedUUID, md : &Module) { +fn codegen_to_file(linker : &Linker, id : NamedUUID, md : &Module) -> Option<()> { let inst = linker.instantiate(id); let module_name = md.link_info.name.deref(); + + if inst.errors.did_error() { + println!("There were errors in {module_name}"); + return None; + } println!("Generating Verilog for {module_name}:"); // gen_ctx.to_circt(); - let Some(code) = gen_verilog_code(md, &inst) else {println!("Error"); return;}; + let code = gen_verilog_code(md, &inst); let mut out_file = File::create(format!("verilog_output/{module_name}.v")).unwrap(); - write!(out_file, "{}", code).unwrap() + write!(out_file, "{}", code).unwrap(); + Some(()) } fn main() -> Result<(), Box> { diff --git a/src/parser.rs b/src/parser.rs index fdc7c28..33b6866 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -229,7 +229,7 @@ struct LeftExpression { impl<'g, 'file> ASTParserContext<'g, 'file> { fn add_global_reference(&mut self, name_span : Span) -> usize { let idx = self.global_references.len(); - self.global_references.push(GlobalReference(name_span, NamedUUID::INVALID)); + self.global_references.push(GlobalReference(name_span, None)); idx } diff --git a/src/typing.rs b/src/typing.rs index 5901a9b..5fcf2de 100644 --- a/src/typing.rs +++ b/src/typing.rs @@ -1,10 +1,11 @@ use std::ops::Deref; -use crate::{ast::{Value, Operator, Span}, linker::{get_builtin_uuid, NamedUUID, Linker, Linkable}, tokenizer::kw, flattening::FlatID, errors::ErrorCollector, arena_alloc::UUID}; +use crate::{ast::{Value, Operator, Span}, linker::{get_builtin_uuid, NamedUUID, Linker, Linkable}, tokenizer::kw, flattening::FlatID, errors::ErrorCollector}; // Types contain everything that cannot be expressed at runtime #[derive(Debug, Clone)] pub enum Type { + Invalid, Named(NamedUUID), /*Contains a wireID pointing to a constant expression for the array size, but doesn't actually take size into account for type checking as that would @@ -13,6 +14,8 @@ pub enum Type { Array(Box<(Type, FlatID)>) } +pub type SpanType = (Type, Span); + impl PartialEq for Type { fn eq(&self, other: &Self) -> bool { match (self, other) { @@ -27,18 +30,18 @@ impl Eq for Type {} impl Type { pub fn to_string(&self, linker : &Linker) -> String { match self { + Type::Invalid => { + "{unknown}".to_owned() + } Type::Named(n) => { - if *n == UUID::INVALID { - "{unknown}".to_owned() - } else { - linker.links[*n].get_full_name() - } + linker.links[*n].get_full_name() } Type::Array(sub) => sub.deref().0.to_string(linker) + "[]", } } pub fn get_root(&self) -> NamedUUID { match self { + Type::Invalid => {unreachable!()} Type::Named(name) => *name, Type::Array(sub) => sub.0.get_root(), } @@ -50,7 +53,7 @@ impl Value { match self { Value::Bool(_) => Type::Named(get_builtin_uuid("bool")), Value::Integer(_) => Type::Named(get_builtin_uuid("int")), - Value::Invalid => Type::Named(UUID::INVALID), + Value::Invalid => Type::Invalid, } } }