From 5a411574c8a4689e8a5ff30033f2a142f147d9b3 Mon Sep 17 00:00:00 2001 From: dalance Date: Tue, 26 Mar 2024 17:51:59 +0900 Subject: [PATCH 1/9] Add UnassignVariable check --- crates/analyzer/src/analyzer.rs | 191 +++++++++++++++- crates/analyzer/src/analyzer_error.rs | 25 +++ .../analyzer/src/handlers/check_assignment.rs | 211 ++++++++++++++---- .../src/handlers/create_symbol_table.rs | 115 ++++++++-- crates/analyzer/src/symbol.rs | 15 +- crates/analyzer/src/symbol_table.rs | 198 +++++++++++----- crates/parser/src/veryl_token.rs | 7 + testcases/sv/02_builtin_type.sv | 18 ++ testcases/sv/04_module.sv | 6 + testcases/sv/06_function.sv | 3 + testcases/sv/07_statement.sv | 1 + testcases/sv/08_generate_declaration.sv | 7 +- testcases/sv/09_struct_enum.sv | 6 +- testcases/sv/10_various_line_comment.sv | 2 + testcases/sv/11_let.sv | 1 + testcases/sv/12_always.sv | 2 + testcases/sv/13_range_operator.sv | 1 + testcases/sv/14_inst.sv | 3 + testcases/sv/15_named_block.sv | 3 + testcases/sv/16_case.sv | 2 + testcases/sv/18_concatenation.sv | 1 + testcases/sv/20_if_case_expression.sv | 2 + testcases/sv/21_cast.sv | 1 + testcases/sv/22_type_modifier.sv | 4 + testcases/sv/23_ifdef.sv | 3 + testcases/sv/24_sv_attribute.sv | 2 + testcases/sv/26_array.sv | 4 + testcases/sv/28_msblsb.sv | 2 + testcases/sv/29_allow.sv | 1 + testcases/sv/35_unconnected_port.sv | 1 + testcases/sv/36_doc_comment.sv | 1 + testcases/sv/45_let_in_always.sv | 1 + testcases/sv/46_let_anywhere.sv | 1 + testcases/veryl/02_builtin_type.veryl | 36 +-- testcases/veryl/04_module.veryl | 9 +- testcases/veryl/06_function.veryl | 6 +- testcases/veryl/07_statement.veryl | 2 +- testcases/veryl/08_generate_declaration.veryl | 6 +- testcases/veryl/09_struct_enum.veryl | 6 +- testcases/veryl/10_various_line_comment.veryl | 2 + testcases/veryl/11_let.veryl | 2 +- testcases/veryl/12_always.veryl | 4 +- testcases/veryl/13_range_operator.veryl | 2 +- testcases/veryl/14_inst.veryl | 6 +- testcases/veryl/15_named_block.veryl | 6 +- testcases/veryl/16_case.veryl | 4 +- testcases/veryl/18_concatenation.veryl | 2 +- testcases/veryl/20_if_case_expression.veryl | 4 +- testcases/veryl/21_cast.veryl | 2 +- testcases/veryl/22_type_modifier.veryl | 8 +- testcases/veryl/23_ifdef.veryl | 6 +- testcases/veryl/24_sv_attribute.veryl | 4 +- testcases/veryl/26_array.veryl | 8 +- testcases/veryl/28_msblsb.veryl | 4 +- testcases/veryl/29_allow.veryl | 2 +- testcases/veryl/35_unconnected_port.veryl | 2 +- testcases/veryl/36_doc_comment.veryl | 4 +- testcases/veryl/45_let_in_always.veryl | 2 +- testcases/veryl/46_let_anywhere.veryl | 2 +- 59 files changed, 781 insertions(+), 201 deletions(-) diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index 4f627ffa..952e390f 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -1,8 +1,10 @@ use crate::analyzer_error::AnalyzerError; use crate::handlers::*; use crate::namespace_table; -use crate::symbol::SymbolKind; -use crate::symbol_table; +use crate::symbol::{ + Direction, ParameterValue, SymbolId, SymbolKind, TypeKind, VariableAffiniation, +}; +use crate::symbol_table::{self, AssignPath, ResolveSymbol}; use std::path::Path; use veryl_metadata::{Lint, Metadata}; use veryl_parser::resource_table; @@ -143,8 +145,191 @@ impl Analyzer { } // check assignment - let _assign_list = symbol_table::get_assign_list(); + let assign_list = symbol_table::get_assign_list(); + let mut assignable_list = Vec::new(); + for symbol in &symbols { + if symbol.token.source == path { + assignable_list.append(&mut traverse_assignable_symbol( + symbol.id, + &AssignPath::new(symbol.id), + )); + } + } + let mut assignable_list: Vec<_> = assignable_list.iter().map(|x| (x, vec![])).collect(); + for assign in &assign_list { + for assignable in &mut assignable_list { + if assignable.0.included(&assign.path) { + assignable.1.push(assign.position.clone()); + } + } + } + + for (path, position) in &assignable_list { + if position.is_empty() { + let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + ret.push(AnalyzerError::unassign_variable( + &symbol.token.to_string(), + text, + &symbol.token, + )); + } + } ret } } + +fn is_assignable(direction: &Direction) -> bool { + matches!( + direction, + Direction::Ref | Direction::Inout | Direction::Output | Direction::Modport + ) +} + +fn traverse_type_symbol(id: SymbolId, path: &AssignPath) -> Vec { + if let Some(symbol) = symbol_table::get(id) { + match &symbol.kind { + SymbolKind::Variable(x) => { + if let TypeKind::UserDefined(ref x) = x.r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + SymbolKind::StructMember(x) => { + if let TypeKind::UserDefined(ref x) = x.r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + SymbolKind::UnionMember(x) => { + if let TypeKind::UserDefined(ref x) = x.r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + SymbolKind::TypeDef(x) => { + if let TypeKind::UserDefined(ref x) = x.r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + SymbolKind::Parameter(x) if x.r#type.kind == TypeKind::Type => { + if let ParameterValue::TypeExpression(TypeExpression::ScalarType(ref x)) = x.value { + let r#type: crate::symbol::Type = (&*x.scalar_type).into(); + if let TypeKind::UserDefined(ref x) = r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + } + SymbolKind::Struct(x) => { + let mut ret = Vec::new(); + for member in &x.members { + let mut path = path.clone(); + path.push(*member); + ret.append(&mut traverse_type_symbol(*member, &path)); + } + return ret; + } + // TODO union support + //SymbolKind::Union(x) => { + // let mut ret = Vec::new(); + // for member in &x.members { + // let mut path = path.clone(); + // path.push(*member); + // ret.append(&mut traverse_type_symbol(*member, &path)); + // } + // return ret; + //} + SymbolKind::Modport(x) => { + let mut ret = Vec::new(); + for member in &x.members { + let mut path = path.clone(); + path.push(*member); + ret.append(&mut traverse_type_symbol(*member, &path)); + } + return ret; + } + SymbolKind::ModportMember(x) if is_assignable(&x.direction) => { + if let Ok(symbol) = symbol_table::resolve(&symbol.token) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } + SymbolKind::Enum(_) => { + return vec![path.clone()]; + } + _ => (), + } + } + + vec![] +} + +fn traverse_assignable_symbol(id: SymbolId, path: &AssignPath) -> Vec { + // check cyclic dependency + if path.0.iter().filter(|x| **x == id).count() > 1 { + return vec![]; + } + + if let Some(symbol) = symbol_table::get(id) { + match &symbol.kind { + SymbolKind::Port(x) if is_assignable(&x.direction) => { + if let Some(ref x) = x.r#type { + if let TypeKind::UserDefined(ref x) = x.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + } + SymbolKind::Variable(x) + if x.affiniation == VariableAffiniation::Module + || x.affiniation == VariableAffiniation::Function => + { + if let TypeKind::UserDefined(ref x) = x.r#type.kind { + if let Ok(symbol) = symbol_table::resolve((x, &symbol.namespace)) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + return traverse_type_symbol(symbol.id, path); + } + } + } else { + return vec![path.clone()]; + } + } + _ => (), + } + } + + vec![] +} diff --git a/crates/analyzer/src/analyzer_error.rs b/crates/analyzer/src/analyzer_error.rs index 48f667e1..f5a19f7b 100644 --- a/crates/analyzer/src/analyzer_error.rs +++ b/crates/analyzer/src/analyzer_error.rs @@ -475,6 +475,23 @@ pub enum AnalyzerError { #[label("Error location")] error_location: SourceSpan, }, + + #[diagnostic( + severity(Warning), + code(unassign_variable), + help(""), + url( + "https://doc.veryl-lang.org/book/06_appendix/02_semantic_error.html#unassign_variable" + ) + )] + #[error("{identifier} is unassigned")] + UnassignVariable { + identifier: String, + #[source_code] + input: NamedSource, + #[label("Error location")] + error_location: SourceSpan, + }, } impl AnalyzerError { @@ -760,4 +777,12 @@ impl AnalyzerError { error_location: token.into(), } } + + pub fn unassign_variable(identifier: &str, source: &str, token: &Token) -> Self { + AnalyzerError::UnassignVariable { + identifier: identifier.to_string(), + input: AnalyzerError::named_source(source, token), + error_location: token.into(), + } + } } diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index 7d442664..da8ca423 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -1,9 +1,8 @@ use crate::analyzer_error::AnalyzerError; -use crate::symbol::Direction as SymDirection; -use crate::symbol::{Symbol, SymbolKind}; -use crate::symbol_table::{self, ResolveSymbol}; +use crate::symbol::{Direction, SymbolId, SymbolKind}; +use crate::symbol_table::{self, AssignPosition, AssignPositionType, ResolveSymbol}; +use std::collections::HashMap; use veryl_parser::veryl_grammar_trait::*; -use veryl_parser::veryl_token::Token; use veryl_parser::veryl_walker::{Handler, HandlerPoint}; use veryl_parser::ParolError; @@ -11,7 +10,7 @@ pub struct CheckAssignment<'a> { pub errors: Vec, text: &'a str, point: HandlerPoint, - current_position: Vec, + assign_position: AssignPosition, in_if_expression: Vec<()>, } @@ -21,7 +20,7 @@ impl<'a> CheckAssignment<'a> { errors: Vec::new(), text, point: HandlerPoint::Before, - current_position: Vec::new(), + assign_position: AssignPosition::default(), in_if_expression: Vec::new(), } } @@ -33,18 +32,42 @@ impl<'a> Handler for CheckAssignment<'a> { } } -fn can_assign(arg: &Symbol) -> bool { - match &arg.kind { - SymbolKind::Variable(_) => true, - SymbolKind::StructMember(_) => true, - SymbolKind::UnionMember(_) => true, - SymbolKind::Port(x) if x.direction == SymDirection::Output => true, - SymbolKind::Port(x) if x.direction == SymDirection::Ref => true, - SymbolKind::Port(x) if x.direction == SymDirection::Inout => true, - SymbolKind::ModportMember(x) if x.direction == SymDirection::Output => true, - SymbolKind::ModportMember(x) if x.direction == SymDirection::Ref => true, - SymbolKind::ModportMember(x) if x.direction == SymDirection::Inout => true, - _ => false, +fn can_assign(full_path: &[SymbolId]) -> bool { + if full_path.is_empty() { + return false; + } + + let leaf_symbol = full_path.last().unwrap(); + if let Some(leaf_symbol) = symbol_table::get(*leaf_symbol) { + match leaf_symbol.kind { + SymbolKind::Variable(_) => true, + SymbolKind::StructMember(_) | SymbolKind::UnionMember(_) => { + let root_symbol = full_path.first().unwrap(); + if let Some(root_symbol) = symbol_table::get(*root_symbol) { + match root_symbol.kind { + SymbolKind::Variable(_) => true, + SymbolKind::Port(x) if x.direction == Direction::Output => true, + SymbolKind::Port(x) if x.direction == Direction::Ref => true, + SymbolKind::Port(x) if x.direction == Direction::Inout => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Output => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Ref => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Inout => true, + _ => false, + } + } else { + false + } + } + SymbolKind::Port(x) if x.direction == Direction::Output => true, + SymbolKind::Port(x) if x.direction == Direction::Ref => true, + SymbolKind::Port(x) if x.direction == Direction::Inout => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Output => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Ref => true, + SymbolKind::ModportMember(x) if x.direction == Direction::Inout => true, + _ => false, + } + } else { + false } } @@ -52,7 +75,14 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn r#else(&mut self, arg: &Else) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if self.in_if_expression.is_empty() { - *self.current_position.last_mut().unwrap() = arg.else_token.token; + let new_position = if let AssignPositionType::StatementBlock(_) = + self.assign_position.0.last().unwrap() + { + AssignPositionType::StatementBlock(arg.else_token.token) + } else { + AssignPositionType::DeclarationBlock(arg.else_token.token) + }; + *self.assign_position.0.last_mut().unwrap() = new_position; } } Ok(()) @@ -73,8 +103,8 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn let_statement(&mut self, arg: &LetStatement) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.current_position.clone(); - position.push(arg.r#let.let_token.token); + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Statement(arg.r#let.let_token.token)); symbol_table::add_assign(x.full_path, position); } } @@ -88,8 +118,8 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { let full_path = x.full_path; match x.found { ResolveSymbol::Symbol(x) => { - if can_assign(&x) { - symbol_table::add_assign(full_path, self.current_position.clone()); + if can_assign(&full_path) { + symbol_table::add_assign(full_path, self.assign_position.clone()); } else { let token = &arg.expression_identifier.identifier.identifier_token.token; @@ -113,10 +143,11 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn if_statement(&mut self, arg: &IfStatement) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position.push(arg.r#if.if_token.token); + self.assign_position + .push(AssignPositionType::StatementBlock(arg.r#if.if_token.token)); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -125,11 +156,24 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn if_reset_statement(&mut self, arg: &IfResetStatement) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position - .push(arg.if_reset.if_reset_token.token); + self.assign_position + .push(AssignPositionType::StatementBlock( + arg.if_reset.if_reset_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); + } + } + Ok(()) + } + + fn for_statement(&mut self, arg: &ForStatement) -> Result<(), ParolError> { + if let HandlerPoint::Before = self.point { + if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Statement(arg.r#for.for_token.token)); + symbol_table::add_assign(x.full_path, position); } } Ok(()) @@ -138,10 +182,13 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn case_item(&mut self, arg: &CaseItem) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position.push(arg.colon.colon_token.token); + self.assign_position + .push(AssignPositionType::StatementBlock( + arg.colon.colon_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -150,8 +197,8 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn let_declaration(&mut self, arg: &LetDeclaration) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.current_position.clone(); - position.push(arg.r#let.let_token.token); + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Declaration(arg.r#let.let_token.token)); symbol_table::add_assign(x.full_path, position); } } @@ -161,11 +208,12 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn always_ff_declaration(&mut self, arg: &AlwaysFfDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position - .push(arg.always_ff.always_ff_token.token); + self.assign_position.push(AssignPositionType::Declaration( + arg.always_ff.always_ff_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -174,11 +222,12 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn always_comb_declaration(&mut self, arg: &AlwaysCombDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position - .push(arg.always_comb.always_comb_token.token); + self.assign_position.push(AssignPositionType::Declaration( + arg.always_comb.always_comb_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -190,9 +239,11 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { let full_path = x.full_path; match x.found { ResolveSymbol::Symbol(x) => { - if can_assign(&x) { - let mut position = self.current_position.clone(); - position.push(arg.assign.assign_token.token); + if can_assign(&full_path) { + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Declaration( + arg.assign.assign_token.token, + )); symbol_table::add_assign(full_path, position); } else { let token = &arg @@ -216,14 +267,68 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { Ok(()) } + fn inst_declaration(&mut self, arg: &InstDeclaration) -> Result<(), ParolError> { + if let HandlerPoint::Before = self.point { + if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { + if let ResolveSymbol::Symbol(ref symbol) = x.found { + if let SymbolKind::Instance(ref x) = symbol.kind { + // get port direction + let mut dirs = HashMap::new(); + let mut dir_unknown = false; + if let Ok(x) = symbol_table::resolve((&x.type_name, &symbol.namespace)) { + if let ResolveSymbol::Symbol(ref symbol) = x.found { + match symbol.kind { + SymbolKind::Module(ref x) => { + for port in &x.ports { + dirs.insert(port.name, port.property.direction); + } + } + SymbolKind::SystemVerilog => dir_unknown = true, + _ => (), + } + } + } + + for (name, target) in &x.connects { + if !target.is_empty() { + let dir_output = if let Some(dir) = dirs.get(name) { + matches!( + dir, + Direction::Ref | Direction::Inout | Direction::Output + ) + } else { + false + }; + + if dir_output | dir_unknown { + if let Ok(x) = + symbol_table::resolve((target, &symbol.namespace)) + { + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Declaration( + arg.inst.inst_token.token, + )); + symbol_table::add_assign(x.full_path, position); + } + } + } + } + } + } + } + } + Ok(()) + } + fn function_declaration(&mut self, arg: &FunctionDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position - .push(arg.function.function_token.token); + self.assign_position.push(AssignPositionType::Declaration( + arg.function.function_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -232,10 +337,24 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn module_if_declaration(&mut self, arg: &ModuleIfDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.current_position.push(arg.r#if.if_token.token); + self.assign_position + .push(AssignPositionType::DeclarationBlock( + arg.r#if.if_token.token, + )); } HandlerPoint::After => { - self.current_position.pop(); + self.assign_position.pop(); + } + } + Ok(()) + } + + fn module_for_declaration(&mut self, arg: &ModuleForDeclaration) -> Result<(), ParolError> { + if let HandlerPoint::Before = self.point { + if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { + let mut position = self.assign_position.clone(); + position.push(AssignPositionType::Statement(arg.r#for.for_token.token)); + symbol_table::add_assign(x.full_path, position); } } Ok(()) diff --git a/crates/analyzer/src/handlers/create_symbol_table.rs b/crates/analyzer/src/handlers/create_symbol_table.rs index ca6065a2..91558d03 100644 --- a/crates/analyzer/src/handlers/create_symbol_table.rs +++ b/crates/analyzer/src/handlers/create_symbol_table.rs @@ -9,10 +9,10 @@ use crate::symbol::{ InterfaceProperty, ModportMemberProperty, ModportProperty, ModuleProperty, PackageProperty, ParameterProperty, ParameterScope, ParameterValue, PortProperty, StructMemberProperty, StructProperty, Symbol, SymbolId, SymbolKind, TypeDefProperty, TypeKind, UnionMemberProperty, - UnionProperty, VariableProperty, + UnionProperty, VariableAffiniation, VariableProperty, }; -use crate::symbol_table; -use std::collections::HashSet; +use crate::symbol_table::{self, SymbolPath}; +use std::collections::{HashMap, HashSet}; use veryl_parser::doc_comment_table; use veryl_parser::resource_table::{self, StrId}; use veryl_parser::veryl_grammar_trait::*; @@ -33,6 +33,9 @@ pub struct CreateSymbolTable<'a> { struct_or_union: Option, enum_members: Vec>, struct_union_members: Vec>, + affiniation: Vec, + connect_targets: Vec>, + connects: HashMap>, } #[derive(Clone)] @@ -110,6 +113,24 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { Ok(()) } + fn expression_identifier(&mut self, arg: &ExpressionIdentifier) -> Result<(), ParolError> { + if let HandlerPoint::Before = self.point { + if let ExpressionIdentifierGroup::ExpressionIdentifierMember(x) = + arg.expression_identifier_group.as_ref() + { + let mut target = Vec::new(); + target.push(arg.identifier.identifier_token.token.text); + + let x = &x.expression_identifier_member; + for x in &x.expression_identifier_member_list0 { + target.push(x.identifier.identifier_token.token.text); + } + self.connect_targets.push(target); + } + } + Ok(()) + } + fn attribute(&mut self, arg: &Attribute) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { self.attribute_lines.insert(arg.hash.hash_token.token.line); @@ -120,7 +141,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { fn let_statement(&mut self, arg: &LetStatement) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { let r#type: SymType = arg.array_type.as_ref().into(); - let property = VariableProperty { r#type }; + let affiniation = self.affiniation.last().cloned().unwrap(); + let property = VariableProperty { + r#type, + affiniation, + }; let kind = SymbolKind::Variable(property); self.insert_symbol(&arg.identifier.identifier_token.token, kind, false); } @@ -136,7 +161,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { self.anonymous_namespace += 1; let r#type: SymType = arg.scalar_type.as_ref().into(); - let property = VariableProperty { r#type }; + let affiniation = self.affiniation.last().cloned().unwrap(); + let property = VariableProperty { + r#type, + affiniation, + }; let kind = SymbolKind::Variable(property); self.insert_symbol(&arg.identifier.identifier_token.token, kind, false); } @@ -148,7 +177,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { fn let_declaration(&mut self, arg: &LetDeclaration) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { let r#type: SymType = arg.array_type.as_ref().into(); - let property = VariableProperty { r#type }; + let affiniation = self.affiniation.last().cloned().unwrap(); + let property = VariableProperty { + r#type, + affiniation, + }; let kind = SymbolKind::Variable(property); self.insert_symbol(&arg.identifier.identifier_token.token, kind, false); } @@ -158,7 +191,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { fn var_declaration(&mut self, arg: &VarDeclaration) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { let r#type: SymType = arg.array_type.as_ref().into(); - let property = VariableProperty { r#type }; + let affiniation = self.affiniation.last().cloned().unwrap(); + let property = VariableProperty { + r#type, + affiniation, + }; let kind = SymbolKind::Variable(property); self.insert_symbol(&arg.identifier.identifier_token.token, kind, false); } @@ -322,18 +359,38 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { } fn inst_declaration(&mut self, arg: &InstDeclaration) -> Result<(), ParolError> { - if let HandlerPoint::Before = self.point { - let mut type_name = vec![arg.scoped_identifier.identifier.identifier_token.token.text]; - for x in &arg.scoped_identifier.scoped_identifier_list { - type_name.push(x.identifier.identifier_token.token.text); - } - let property = InstanceProperty { type_name }; + if let HandlerPoint::After = self.point { + let type_name: SymbolPath = arg.scoped_identifier.as_ref().into(); + let type_name = type_name.to_vec(); + let connects = self.connects.drain().collect(); + let property = InstanceProperty { + type_name, + connects, + }; let kind = SymbolKind::Instance(property); self.insert_symbol(&arg.identifier.identifier_token.token, kind, false); } Ok(()) } + fn inst_port_item(&mut self, arg: &InstPortItem) -> Result<(), ParolError> { + match self.point { + HandlerPoint::Before => self.connect_targets.clear(), + HandlerPoint::After => { + let port = arg.identifier.identifier_token.token.text; + let targets = if arg.inst_port_item_opt.is_some() { + self.connect_targets.drain(0..).collect() + } else { + vec![vec![port]] + }; + for target in targets { + self.connects.insert(port, target); + } + } + } + Ok(()) + } + fn with_parameter_item(&mut self, arg: &WithParameterItem) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { let token = arg.identifier.identifier_token.token; @@ -440,9 +497,13 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { ); let name = arg.identifier.identifier_token.token.text; - self.namespace.push(name) + self.namespace.push(name); + self.affiniation.push(VariableAffiniation::Function); + } + HandlerPoint::After => { + self.namespace.pop(); + self.affiniation.pop(); } - HandlerPoint::After => self.namespace.pop(), } Ok(()) } @@ -483,9 +544,13 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { ); let name = arg.identifier.identifier_token.token.text; - self.namespace.push(name) + self.namespace.push(name); + self.affiniation.push(VariableAffiniation::Module); + } + HandlerPoint::After => { + self.namespace.pop(); + self.affiniation.pop(); } - HandlerPoint::After => self.namespace.pop(), } Ok(()) } @@ -574,9 +639,13 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { ); let name = arg.identifier.identifier_token.token.text; - self.namespace.push(name) + self.namespace.push(name); + self.affiniation.push(VariableAffiniation::Intarface); + } + HandlerPoint::After => { + self.namespace.pop(); + self.affiniation.pop(); } - HandlerPoint::After => self.namespace.pop(), } Ok(()) } @@ -658,9 +727,13 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { ); let name = arg.identifier.identifier_token.token.text; - self.namespace.push(name) + self.namespace.push(name); + self.affiniation.push(VariableAffiniation::Package); + } + HandlerPoint::After => { + self.namespace.pop(); + self.affiniation.pop(); } - HandlerPoint::After => self.namespace.pop(), } Ok(()) } diff --git a/crates/analyzer/src/symbol.rs b/crates/analyzer/src/symbol.rs index 5b7b947a..0a849e22 100644 --- a/crates/analyzer/src/symbol.rs +++ b/crates/analyzer/src/symbol.rs @@ -1,6 +1,7 @@ use crate::evaluator::{Evaluated, Evaluator}; use crate::namespace::Namespace; use std::cell::{Cell, RefCell}; +use std::collections::HashMap; use std::fmt; use veryl_parser::resource_table::StrId; use veryl_parser::veryl_grammar_trait as syntax_tree; @@ -258,7 +259,7 @@ impl fmt::Display for SymbolKind { } } -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum Direction { Input, Output, @@ -302,7 +303,7 @@ pub struct Type { pub array: Vec, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq, Eq)] pub enum TypeKind { Bit, Logic, @@ -463,6 +464,15 @@ impl From<&syntax_tree::ArrayType> for Type { #[derive(Debug, Clone)] pub struct VariableProperty { pub r#type: Type, + pub affiniation: VariableAffiniation, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum VariableAffiniation { + Module, + Intarface, + Package, + Function, } #[derive(Debug, Clone)] @@ -613,6 +623,7 @@ pub struct FunctionProperty { #[derive(Debug, Clone)] pub struct InstanceProperty { pub type_name: Vec, + pub connects: HashMap>, } #[derive(Debug, Clone)] diff --git a/crates/analyzer/src/symbol_table.rs b/crates/analyzer/src/symbol_table.rs index 62c7276f..a0d36e68 100644 --- a/crates/analyzer/src/symbol_table.rs +++ b/crates/analyzer/src/symbol_table.rs @@ -1,7 +1,7 @@ use crate::evaluator::Evaluated; use crate::namespace::Namespace; use crate::namespace_table; -use crate::symbol::{Direction, DocComment, Symbol, SymbolId, SymbolKind, TypeKind}; +use crate::symbol::{DocComment, Symbol, SymbolId, SymbolKind, TypeKind}; use std::cell::RefCell; use std::collections::HashMap; use std::fmt; @@ -32,6 +32,10 @@ impl SymbolPath { pub fn as_slice(&self) -> &[StrId] { self.0.as_slice() } + + pub fn to_vec(self) -> Vec { + self.0 + } } impl fmt::Display for SymbolPath { @@ -151,6 +155,13 @@ impl From<(&SymbolPath, &Namespace)> for SymbolPathNamespace { } } +impl From<(&Vec, &Namespace)> for SymbolPathNamespace { + fn from(value: (&Vec, &Namespace)) -> Self { + let path = SymbolPath::new(value.0); + SymbolPathNamespace(path, value.1.clone()) + } +} + impl From<&syntax_tree::Identifier> for SymbolPathNamespace { fn from(value: &syntax_tree::Identifier) -> Self { let namespace = namespace_table::get(value.identifier_token.token.id).unwrap(); @@ -221,8 +232,102 @@ impl ResolveError { #[derive(Clone, Debug)] pub struct Assign { - full_path: Vec, - position: Vec, + pub path: AssignPath, + pub position: AssignPosition, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct AssignPath(pub Vec); + +impl AssignPath { + pub fn new(x: SymbolId) -> Self { + Self(vec![x]) + } + + pub fn push(&mut self, x: SymbolId) { + self.0.push(x) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } + + pub fn included(&self, x: &AssignPath) -> bool { + for (i, x) in x.0.iter().enumerate() { + if let Some(path) = self.0.get(i) { + if path != x { + return false; + } + } else { + return false; + } + } + true + } +} + +impl fmt::Display for AssignPath { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ret = "".to_string(); + for (i, id) in self.0.iter().enumerate() { + if let Some(symbol) = get(*id) { + if i != 0 { + ret.push('.'); + } + ret.push_str(&symbol.token.to_string()); + } + } + ret.fmt(f) + } +} + +#[derive(Clone, Default, Debug)] +pub struct AssignPosition(pub Vec); + +impl AssignPosition { + pub fn new(x: AssignPositionType) -> Self { + Self(vec![x]) + } + + pub fn push(&mut self, x: AssignPositionType) { + self.0.push(x) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } +} + +impl fmt::Display for AssignPosition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ret = "".to_string(); + for (i, x) in self.0.iter().enumerate() { + if i != 0 { + ret.push('.'); + } + ret.push_str(&x.token().to_string()); + } + ret.fmt(f) + } +} + +#[derive(Clone, Debug)] +pub enum AssignPositionType { + DeclarationBlock(Token), + Declaration(Token), + StatementBlock(Token), + Statement(Token), +} + +impl AssignPositionType { + pub fn token(&self) -> &Token { + match self { + AssignPositionType::DeclarationBlock(x) => x, + AssignPositionType::Declaration(x) => x, + AssignPositionType::StatementBlock(x) => x, + AssignPositionType::Statement(x) => x, + } + } } #[derive(Clone, Default, Debug)] @@ -377,6 +482,30 @@ impl SymbolTable { } } } + SymbolKind::Port(ref x) => { + if let Some(ref x) = x.r#type { + if let TypeKind::UserDefined(ref x) = x.kind { + let path = SymbolPath::new(x); + if let Ok(symbol) = self.resolve(&path, &namespace) { + if let ResolveSymbol::Symbol(symbol) = symbol.found { + namespace = Namespace::new(); + for path in &symbol.namespace.paths { + namespace.push(*path); + } + namespace.push(symbol.token.text); + inner = true; + } else { + unreachable!(); + } + } else { + return Ok(ResolveResult { + found: ResolveSymbol::External, + full_path, + }); + } + } + } + } SymbolKind::Module(_) => { if other_prj & !ret.public { return Err(ResolveError::new( @@ -419,17 +548,6 @@ impl SymbolTable { } inner = true; } - SymbolKind::Port(ref x) if x.direction == Direction::Modport => { - if let Some(ref x) = x.r#type { - if let TypeKind::UserDefined(ref x) = x.kind { - namespace = Namespace::default(); - for x in x { - namespace.push(*x); - } - inner = true; - } - } - } SymbolKind::SystemVerilog => { namespace = ret.namespace.clone(); namespace.push(ret.token.text); @@ -492,53 +610,17 @@ impl SymbolTable { let mut path_width = 0; let mut pos_width = 0; for assign in &self.assign_list { - path_width = path_width.max( - assign - .full_path - .iter() - .map(|x| self.symbol_table.get(x).unwrap()) - .map(|x| format!("{}", x.token.text).len()) - .sum::() - + assign.full_path.len() - - 1, - ); - pos_width = pos_width.max( - assign - .position - .iter() - .map(|x| x.to_string().len()) - .sum::() - + assign.position.len() - - 1, - ); + path_width = path_width.max(assign.path.to_string().len()); + pos_width = pos_width.max(assign.position.to_string().len()); } for assign in &self.assign_list { - let mut path = "".to_string(); - for (i, x) in assign.full_path.iter().enumerate() { - let x = self.symbol_table.get(x).unwrap(); - if i == 0 { - path.push_str(&x.token.to_string()); - } else { - path.push_str(&format!(".{}", x.token)); - } - } - - let mut pos = "".to_string(); - for (i, x) in assign.position.iter().enumerate() { - if i == 0 { - pos.push_str(&x.to_string()); - } else { - pos.push_str(&format!(".{}", x)); - } - } - - let last_token = assign.position.last().unwrap(); + let last_token = assign.position.0.last().unwrap().token(); ret.push_str(&format!( " {:path_width$} / {:pos_width$} @ {}:{}:{}\n", - path, - pos, + assign.path, + assign.position, last_token.source, last_token.line, last_token.column, @@ -608,9 +690,9 @@ impl SymbolTable { self.project_local_table.get(&prj).cloned() } - pub fn add_assign(&mut self, full_path: Vec, position: Vec) { + pub fn add_assign(&mut self, full_path: Vec, position: AssignPosition) { let assign = Assign { - full_path, + path: AssignPath(full_path), position, }; self.assign_list.push(assign); @@ -925,7 +1007,7 @@ pub fn get_project_local(prj: StrId) -> Option> { SYMBOL_TABLE.with(|f| f.borrow().get_project_local(prj)) } -pub fn add_assign(full_path: Vec, position: Vec) { +pub fn add_assign(full_path: Vec, position: AssignPosition) { SYMBOL_TABLE.with(|f| f.borrow_mut().add_assign(full_path, position)) } diff --git a/crates/parser/src/veryl_token.rs b/crates/parser/src/veryl_token.rs index 83ba1c0a..daba6d42 100644 --- a/crates/parser/src/veryl_token.rs +++ b/crates/parser/src/veryl_token.rs @@ -145,6 +145,13 @@ pub struct VerylToken { } impl VerylToken { + pub fn new(token: Token) -> Self { + Self { + token, + comments: vec![], + } + } + pub fn replace(&self, text: &str) -> Self { let length = text.len(); let text = resource_table::insert_str(text); diff --git a/testcases/sv/02_builtin_type.sv b/testcases/sv/02_builtin_type.sv index 9b4df6c2..2aa5ba60 100644 --- a/testcases/sv/02_builtin_type.sv +++ b/testcases/sv/02_builtin_type.sv @@ -1,31 +1,49 @@ module veryl_testcase_Module02; // unsigned integer int unsigned _a ; + always_comb _a = 1; longint unsigned _aa; + always_comb _aa = 1; // signed integer int signed _b ; + always_comb _b = 1; longint signed _bb; + always_comb _bb = 1; // floating point shortreal _c ; + always_comb _c = 1; real _cc; + always_comb _cc = 1; // 4 state (01xz) type logic _d ; + always_comb _d = 1; logic [10-1:0] _dd ; + always_comb _dd = 1; logic [10-1:0][10-1:0] _ddd; + always_comb _ddd = 1; // 2 state (01) type bit _e ; + always_comb _e = 1; bit [10-1:0] _ee ; + always_comb _ee = 1; bit [10-1:0][10-1:0] _eee; + always_comb _eee = 1; // array int unsigned _f [0:10-1]; + always_comb _f = 1; longint unsigned _ff [0:10-1]; + always_comb _ff = 1; int signed _fff [0:10-1]; + always_comb _fff = 1; longint signed _ffff [0:10-1]; + always_comb _ffff = 1; shortreal _fffff [0:10-1]; + always_comb _fffff = 1; real _ffffff [0:10-1]; + always_comb _ffffff = 1; endmodule diff --git a/testcases/sv/04_module.sv b/testcases/sv/04_module.sv index e0014b4c..7ed16b1f 100644 --- a/testcases/sv/04_module.sv +++ b/testcases/sv/04_module.sv @@ -18,8 +18,14 @@ module veryl_testcase_Module04 #( // variable declaration logic _d ; + always_comb _d = 1; logic [10-1:0] _dd ; + always_comb _dd = 1; bit [10-1:0][10-1:0] _ddd; + always_comb _ddd = 1; + + always_comb bb = 0; + always_comb bbb = 0; endmodule interface veryl_testcase_Interface04; diff --git a/testcases/sv/06_function.sv b/testcases/sv/06_function.sv index 5fe527c9..a3efdea8 100644 --- a/testcases/sv/06_function.sv +++ b/testcases/sv/06_function.sv @@ -38,8 +38,11 @@ module veryl_testcase_Module06; endfunction logic [ParamX-1:0] a; + always_comb a = 1; logic [ParamX-1:0] b; + always_comb b = 1; logic [ParamX-1:0] c; + always_comb c = 1; logic [ParamX-1:0] d; // function call diff --git a/testcases/sv/07_statement.sv b/testcases/sv/07_statement.sv index 7d52a0a0..178eaed0 100644 --- a/testcases/sv/07_statement.sv +++ b/testcases/sv/07_statement.sv @@ -2,6 +2,7 @@ module veryl_testcase_Module07; logic a ; logic aa ; logic clk; + always_comb clk = 1; always_comb begin // assignment statement diff --git a/testcases/sv/08_generate_declaration.sv b/testcases/sv/08_generate_declaration.sv index c338aa8c..21902e6d 100644 --- a/testcases/sv/08_generate_declaration.sv +++ b/testcases/sv/08_generate_declaration.sv @@ -1,7 +1,8 @@ module veryl_testcase_Module08; - logic a ; - logic b ; - logic i_clk; + localparam int unsigned a = 1; + localparam int unsigned b = 1; + logic i_clk; + always_comb i_clk = 1; // if declaration if (a == 1) begin :label diff --git a/testcases/sv/09_struct_enum.sv b/testcases/sv/09_struct_enum.sv index 40b983ef..dea57fb3 100644 --- a/testcases/sv/09_struct_enum.sv +++ b/testcases/sv/09_struct_enum.sv @@ -16,6 +16,8 @@ module veryl_testcase_Module09; A a; B b; - always_comb a.a = 1; - always_comb b = B_X; + always_comb a.a = 1; + always_comb a.aa = 1; + always_comb a.aaa = 1; + always_comb b = B_X; endmodule diff --git a/testcases/sv/10_various_line_comment.sv b/testcases/sv/10_various_line_comment.sv index 343a1007..b29dbfc8 100644 --- a/testcases/sv/10_various_line_comment.sv +++ b/testcases/sv/10_various_line_comment.sv @@ -21,6 +21,8 @@ module //a ; logic [2-1:0] up_down; + always_comb o_count = count; + always_comb // a begin up_down = // a diff --git a/testcases/sv/11_let.sv b/testcases/sv/11_let.sv index 11d985f0..cf375fd5 100644 --- a/testcases/sv/11_let.sv +++ b/testcases/sv/11_let.sv @@ -3,6 +3,7 @@ module veryl_testcase_Module11; logic b ; logic [10-1:0] bb ; bit [10-1:0][10-1:0] _bbb; + always_comb _bbb = 1; // variable declaration with assignment logic [10-1:0] _c; diff --git a/testcases/sv/12_always.sv b/testcases/sv/12_always.sv index fbc566be..2019cfec 100644 --- a/testcases/sv/12_always.sv +++ b/testcases/sv/12_always.sv @@ -5,7 +5,9 @@ module veryl_testcase_Module12 ( logic a ; logic aa; logic b ; + always_comb b = 1; logic c ; + always_comb c = 1; // always_ff declaration with default polarity always_ff @ (posedge i_clk, negedge i_rst) begin diff --git a/testcases/sv/13_range_operator.sv b/testcases/sv/13_range_operator.sv index 9a5a22ca..021ac39b 100644 --- a/testcases/sv/13_range_operator.sv +++ b/testcases/sv/13_range_operator.sv @@ -1,6 +1,7 @@ module veryl_testcase_Module13; logic a; logic X; + always_comb X = 1; // bit select always_comb a = X[0]; diff --git a/testcases/sv/14_inst.sv b/testcases/sv/14_inst.sv index 81197be7..c0b5f4e6 100644 --- a/testcases/sv/14_inst.sv +++ b/testcases/sv/14_inst.sv @@ -1,7 +1,10 @@ module veryl_testcase_Module14; logic a ; + always_comb a = 1; logic aa ; + always_comb aa = 1; logic bbb; + always_comb bbb = 1; // module instantiation veryl_testcase_Module14B x (); diff --git a/testcases/sv/15_named_block.sv b/testcases/sv/15_named_block.sv index a9b349ae..53693591 100644 --- a/testcases/sv/15_named_block.sv +++ b/testcases/sv/15_named_block.sv @@ -1,11 +1,14 @@ module veryl_testcase_Module15; logic _a; + always_comb _a = 1; if (1) begin :label logic _a; + always_comb _a = 1; end if (1) begin :label1 logic _a; + always_comb _a = 1; end endmodule diff --git a/testcases/sv/16_case.sv b/testcases/sv/16_case.sv index 799f6b1f..38bca5f2 100644 --- a/testcases/sv/16_case.sv +++ b/testcases/sv/16_case.sv @@ -1,7 +1,9 @@ module veryl_testcase_Module16; logic a; logic x; + always_comb x = 1; logic y; + always_comb y = 1; always_comb begin case (x) diff --git a/testcases/sv/18_concatenation.sv b/testcases/sv/18_concatenation.sv index 586667e2..7a8527f4 100644 --- a/testcases/sv/18_concatenation.sv +++ b/testcases/sv/18_concatenation.sv @@ -1,6 +1,7 @@ module veryl_testcase_Module18; logic a; logic b; + always_comb b = 1; always_comb a = {a[10:0], b}; always_comb a = {{10{a[10:0]}}, {4{b}}}; diff --git a/testcases/sv/20_if_case_expression.sv b/testcases/sv/20_if_case_expression.sv index fc2dd3b5..42d49bfe 100644 --- a/testcases/sv/20_if_case_expression.sv +++ b/testcases/sv/20_if_case_expression.sv @@ -2,7 +2,9 @@ module veryl_testcase_Module20; logic a; logic b; logic x; + always_comb x = 1; logic y; + always_comb y = 1; always_comb a = ((x) ? ( 1 diff --git a/testcases/sv/21_cast.sv b/testcases/sv/21_cast.sv index 5292e341..a4207692 100644 --- a/testcases/sv/21_cast.sv +++ b/testcases/sv/21_cast.sv @@ -1,6 +1,7 @@ module veryl_testcase_Module21; logic a; logic b; + always_comb b = 1; typedef enum logic { EnumA_A, diff --git a/testcases/sv/22_type_modifier.sv b/testcases/sv/22_type_modifier.sv index c3b3507d..ce78d84a 100644 --- a/testcases/sv/22_type_modifier.sv +++ b/testcases/sv/22_type_modifier.sv @@ -1,6 +1,10 @@ module veryl_testcase_Module22; logic signed [10-1:0] _a; + always_comb _a = 1; tri logic signed [10-1:0] _b; + always_comb _b = 1; tri logic signed [10-1:0] _c; + always_comb _c = 1; logic [10-1:0] _d; + always_comb _d = 1; endmodule diff --git a/testcases/sv/23_ifdef.sv b/testcases/sv/23_ifdef.sv index 67d3163d..74d19a0a 100644 --- a/testcases/sv/23_ifdef.sv +++ b/testcases/sv/23_ifdef.sv @@ -26,11 +26,14 @@ module veryl_testcase_Module23 #( `ifdef DEFINE_A `ifdef DEFINE_B logic [10-1:0] _a; + always_comb _a = 1; `endif `endif `ifdef DEFINE_A logic [10-1:0] _b; + always_comb _b = 1; logic [10-1:0] _c; + always_comb _c = 1; `endif endmodule diff --git a/testcases/sv/24_sv_attribute.sv b/testcases/sv/24_sv_attribute.sv index 720409fa..75334046 100644 --- a/testcases/sv/24_sv_attribute.sv +++ b/testcases/sv/24_sv_attribute.sv @@ -1,6 +1,8 @@ module veryl_testcase_Module24; (* ram_style="block" *) logic _a; + always_comb _a = 1; (* mark_debug="true" *) logic _b; + always_comb _b = 1; endmodule diff --git a/testcases/sv/26_array.sv b/testcases/sv/26_array.sv index 93044352..722ac6d1 100644 --- a/testcases/sv/26_array.sv +++ b/testcases/sv/26_array.sv @@ -1,6 +1,10 @@ module veryl_testcase_Module26; logic [10-1:0] _a ; + always_comb _a = 1; logic [10-1:0][10-1:0] _b ; + always_comb _b = 1; logic [10-1:0][10-1:0] _c [0:10-1] ; + always_comb _c = 1; logic [10-1:0][10-1:0] _d [0:10-1][0:10-1]; + always_comb _d = 1; endmodule diff --git a/testcases/sv/28_msblsb.sv b/testcases/sv/28_msblsb.sv index 55bb6996..e7e73d51 100644 --- a/testcases/sv/28_msblsb.sv +++ b/testcases/sv/28_msblsb.sv @@ -3,7 +3,9 @@ module veryl_testcase_Module28; localparam int unsigned WIDTH1 = 20; logic [10-1:0][20-1:0] a; + always_comb a = 1; logic [WIDTH0 + 10-1:0][WIDTH1-1:0] b; + always_comb b = 1; logic _x; always_comb _x = a[((10) - 1)][((20) - 1):0 + 1]; diff --git a/testcases/sv/29_allow.sv b/testcases/sv/29_allow.sv index 9fa84ec7..7fdcd8cf 100644 --- a/testcases/sv/29_allow.sv +++ b/testcases/sv/29_allow.sv @@ -5,6 +5,7 @@ module veryl_testcase_Module29 ( logic a; logic b; logic c; + always_comb c = 1; always_ff @ (posedge clk, negedge rst) begin if (!rst) begin a <= 0; diff --git a/testcases/sv/35_unconnected_port.sv b/testcases/sv/35_unconnected_port.sv index d577711b..51889ac0 100644 --- a/testcases/sv/35_unconnected_port.sv +++ b/testcases/sv/35_unconnected_port.sv @@ -1,5 +1,6 @@ module veryl_testcase_Module35; logic aa; + always_comb aa = 1; veryl_testcase_Module35B xx ( .aa (aa), diff --git a/testcases/sv/36_doc_comment.sv b/testcases/sv/36_doc_comment.sv index ec73bb0e..a515c218 100644 --- a/testcases/sv/36_doc_comment.sv +++ b/testcases/sv/36_doc_comment.sv @@ -12,6 +12,7 @@ module veryl_testcase_Module36 #( input logic [ParamA-1:0] i_data , /// Data input output logic [ParamA-1:0] o_data /// Data output ); + always_comb o_data = 0; endmodule /// Test interface for doc comment diff --git a/testcases/sv/45_let_in_always.sv b/testcases/sv/45_let_in_always.sv index 80114c7f..ff28ce19 100644 --- a/testcases/sv/45_let_in_always.sv +++ b/testcases/sv/45_let_in_always.sv @@ -1,5 +1,6 @@ module veryl_testcase_Module45; logic a; + always_comb a = 1; logic [10-1:0] b; logic [10-1:0] c; diff --git a/testcases/sv/46_let_anywhere.sv b/testcases/sv/46_let_anywhere.sv index a12226c3..f25e8751 100644 --- a/testcases/sv/46_let_anywhere.sv +++ b/testcases/sv/46_let_anywhere.sv @@ -1,5 +1,6 @@ module veryl_testcase_Module46; logic a; + always_comb a = 1; logic [10-1:0] b; logic [10-1:0] c; logic [10-1:0] d; diff --git a/testcases/veryl/02_builtin_type.veryl b/testcases/veryl/02_builtin_type.veryl index ae22ab95..959fa40e 100644 --- a/testcases/veryl/02_builtin_type.veryl +++ b/testcases/veryl/02_builtin_type.veryl @@ -1,31 +1,31 @@ module Module02 { // unsigned integer - var _a : u32; - var _aa: u64; + let _a : u32 = 1; + let _aa: u64 = 1; // signed integer - var _b : i32; - var _bb: i64; + let _b : i32 = 1; + let _bb: i64 = 1; // floating point - var _c : f32; - var _cc: f64; + let _c : f32 = 1; + let _cc: f64 = 1; // 4 state (01xz) type - var _d : logic ; - var _dd : logic<10> ; - var _ddd: logic<10, 10>; + let _d : logic = 1; + let _dd : logic<10> = 1; + let _ddd: logic<10, 10> = 1; // 2 state (01) type - var _e : bit ; - var _ee : bit<10> ; - var _eee: bit<10, 10>; + let _e : bit = 1; + let _ee : bit<10> = 1; + let _eee: bit<10, 10> = 1; // array - var _f : u32 [10]; - var _ff : u64 [10]; - var _fff : i32 [10]; - var _ffff : i64 [10]; - var _fffff : f32 [10]; - var _ffffff: f64 [10]; + let _f : u32 [10] = 1; + let _ff : u64 [10] = 1; + let _fff : i32 [10] = 1; + let _ffff : i64 [10] = 1; + let _fffff : f32 [10] = 1; + let _ffffff: f64 [10] = 1; } diff --git a/testcases/veryl/04_module.veryl b/testcases/veryl/04_module.veryl index 536de800..d9e11c5a 100644 --- a/testcases/veryl/04_module.veryl +++ b/testcases/veryl/04_module.veryl @@ -17,9 +17,12 @@ module Module04 #( local cc: u64 = 1; // variable declaration - var _d : logic ; - var _dd : logic<10> ; - var _ddd: bit <10, 10>; + let _d : logic = 1; + let _dd : logic<10> = 1; + let _ddd: bit <10, 10> = 1; + + assign bb = 0; + assign bbb = 0; } interface Interface04 { diff --git a/testcases/veryl/06_function.veryl b/testcases/veryl/06_function.veryl index b0db00c2..dbf7d4a8 100644 --- a/testcases/veryl/06_function.veryl +++ b/testcases/veryl/06_function.veryl @@ -34,9 +34,9 @@ module Module06 { c = a / 1; } - var a: logic; - var b: logic; - var c: logic; + let a: logic = 1; + let b: logic = 1; + let c: logic = 1; var d: logic; // function call diff --git a/testcases/veryl/07_statement.veryl b/testcases/veryl/07_statement.veryl index 404c09c0..79fbb01a 100644 --- a/testcases/veryl/07_statement.veryl +++ b/testcases/veryl/07_statement.veryl @@ -1,7 +1,7 @@ module Module07 { var a : logic; var aa : logic; - var clk: logic; + let clk: logic = 1; always_comb { // assignment statement diff --git a/testcases/veryl/08_generate_declaration.veryl b/testcases/veryl/08_generate_declaration.veryl index 47e0b3bf..35eec16e 100644 --- a/testcases/veryl/08_generate_declaration.veryl +++ b/testcases/veryl/08_generate_declaration.veryl @@ -1,7 +1,7 @@ module Module08 { - var a : logic; - var b : logic; - var i_clk: logic; + local a : u32 = 1; + local b : u32 = 1; + let i_clk: logic = 1; // if declaration if a == 1 :label { diff --git a/testcases/veryl/09_struct_enum.veryl b/testcases/veryl/09_struct_enum.veryl index 560bef2e..8304841a 100644 --- a/testcases/veryl/09_struct_enum.veryl +++ b/testcases/veryl/09_struct_enum.veryl @@ -16,6 +16,8 @@ module Module09 { var a: A; var b: B; - assign a.a = 1; - assign b = B::X; + assign a.a = 1; + assign a.aa = 1; + assign a.aaa = 1; + assign b = B::X; } diff --git a/testcases/veryl/10_various_line_comment.veryl b/testcases/veryl/10_various_line_comment.veryl index 448b4c91..2afcb5f6 100644 --- a/testcases/veryl/10_various_line_comment.veryl +++ b/testcases/veryl/10_various_line_comment.veryl @@ -20,6 +20,8 @@ module //a ; var up_down: logic<2>; + assign o_count = count; + always_comb // a { up_down = // a diff --git a/testcases/veryl/11_let.veryl b/testcases/veryl/11_let.veryl index 5295e74a..12e2e4e8 100644 --- a/testcases/veryl/11_let.veryl +++ b/testcases/veryl/11_let.veryl @@ -2,7 +2,7 @@ module Module11 { // variable declaration var b : logic ; var bb : logic<10> ; - var _bbb: bit <10, 10>; + let _bbb: bit <10, 10> = 1; // variable declaration with assignment let _c: logic<10> = 1; diff --git a/testcases/veryl/12_always.veryl b/testcases/veryl/12_always.veryl index 26a95438..ae8d85b2 100644 --- a/testcases/veryl/12_always.veryl +++ b/testcases/veryl/12_always.veryl @@ -4,8 +4,8 @@ module Module12 ( ) { var a : logic; var aa: logic; - var b : logic; - var c : logic; + let b : logic = 1; + let c : logic = 1; // always_ff declaration with default polarity always_ff (i_clk, i_rst) { diff --git a/testcases/veryl/13_range_operator.veryl b/testcases/veryl/13_range_operator.veryl index 129b36d5..00a63d6a 100644 --- a/testcases/veryl/13_range_operator.veryl +++ b/testcases/veryl/13_range_operator.veryl @@ -1,6 +1,6 @@ module Module13 { var a: logic; - var X: logic; + let X: logic = 1; // bit select assign a = X[0]; diff --git a/testcases/veryl/14_inst.veryl b/testcases/veryl/14_inst.veryl index 17f0f0e3..c5ad3378 100644 --- a/testcases/veryl/14_inst.veryl +++ b/testcases/veryl/14_inst.veryl @@ -1,7 +1,7 @@ module Module14 { - var a : logic; - var aa : logic; - var bbb: logic; + let a : logic = 1; + let aa : logic = 1; + let bbb: logic = 1; // module instantiation inst x: Module14B; diff --git a/testcases/veryl/15_named_block.veryl b/testcases/veryl/15_named_block.veryl index e2f59141..c41f4e3e 100644 --- a/testcases/veryl/15_named_block.veryl +++ b/testcases/veryl/15_named_block.veryl @@ -1,11 +1,11 @@ module Module15 { - var _a: logic; + let _a: logic = 1; :label { - var _a: logic; + let _a: logic = 1; } :label1 { - var _a: logic; + let _a: logic = 1; } } diff --git a/testcases/veryl/16_case.veryl b/testcases/veryl/16_case.veryl index d8e0d7dd..a755e03f 100644 --- a/testcases/veryl/16_case.veryl +++ b/testcases/veryl/16_case.veryl @@ -1,7 +1,7 @@ module Module16 { var a: logic; - var x: logic; - var y: logic; + let x: logic = 1; + let y: logic = 1; always_comb { case x { diff --git a/testcases/veryl/18_concatenation.veryl b/testcases/veryl/18_concatenation.veryl index 853fd206..4540cdd4 100644 --- a/testcases/veryl/18_concatenation.veryl +++ b/testcases/veryl/18_concatenation.veryl @@ -1,6 +1,6 @@ module Module18 { var a: logic; - var b: logic; + let b: logic = 1; assign a = {a[10:0], b}; assign a = {a[10:0] repeat 10, b repeat 4}; diff --git a/testcases/veryl/20_if_case_expression.veryl b/testcases/veryl/20_if_case_expression.veryl index 55853315..90bfd841 100644 --- a/testcases/veryl/20_if_case_expression.veryl +++ b/testcases/veryl/20_if_case_expression.veryl @@ -1,8 +1,8 @@ module Module20 { var a: logic; var b: logic; - var x: logic; - var y: logic; + let x: logic = 1; + let y: logic = 1; assign a = if x { 1 diff --git a/testcases/veryl/21_cast.veryl b/testcases/veryl/21_cast.veryl index 42c55b39..cc794c8d 100644 --- a/testcases/veryl/21_cast.veryl +++ b/testcases/veryl/21_cast.veryl @@ -1,6 +1,6 @@ module Module21 { var a: logic; - var b: logic; + let b: logic = 1; enum EnumA: logic { A, diff --git a/testcases/veryl/22_type_modifier.veryl b/testcases/veryl/22_type_modifier.veryl index f2317151..5d803554 100644 --- a/testcases/veryl/22_type_modifier.veryl +++ b/testcases/veryl/22_type_modifier.veryl @@ -1,6 +1,6 @@ module Module22 { - var _a: signed logic <10>; - var _b: tri signed logic<10>; - var _c: signed tri logic<10>; - var _d: logic <10>; + let _a: signed logic <10> = 1; + let _b: tri signed logic<10> = 1; + let _c: signed tri logic<10> = 1; + let _d: logic <10> = 1; } diff --git a/testcases/veryl/23_ifdef.veryl b/testcases/veryl/23_ifdef.veryl index 43c66523..7f583c60 100644 --- a/testcases/veryl/23_ifdef.veryl +++ b/testcases/veryl/23_ifdef.veryl @@ -21,12 +21,12 @@ module Module23 #( ) { #[ifdef(DEFINE_A)] #[ifdef(DEFINE_B)] - var _a: logic<10>; + let _a: logic<10> = 1; #[ifdef(DEFINE_A)] { - var _b: logic<10>; - var _c: logic<10>; + let _b: logic<10> = 1; + let _c: logic<10> = 1; } } diff --git a/testcases/veryl/24_sv_attribute.veryl b/testcases/veryl/24_sv_attribute.veryl index a9a1a52d..44b2429e 100644 --- a/testcases/veryl/24_sv_attribute.veryl +++ b/testcases/veryl/24_sv_attribute.veryl @@ -1,6 +1,6 @@ module Module24 { #[sv("ram_style=\"block\"")] - var _a: logic; + let _a: logic = 1; #[sv("mark_debug=\"true\"")] - var _b: logic; + let _b: logic = 1; } diff --git a/testcases/veryl/26_array.veryl b/testcases/veryl/26_array.veryl index 3be83541..e9d713aa 100644 --- a/testcases/veryl/26_array.veryl +++ b/testcases/veryl/26_array.veryl @@ -1,6 +1,6 @@ module Module26 { - var _a: logic<10> ; - var _b: logic<10, 10> ; - var _c: logic<10, 10> [10] ; - var _d: logic<10, 10> [10, 10]; + let _a: logic<10> = 1; + let _b: logic<10, 10> = 1; + let _c: logic<10, 10> [10] = 1; + let _d: logic<10, 10> [10, 10] = 1; } diff --git a/testcases/veryl/28_msblsb.veryl b/testcases/veryl/28_msblsb.veryl index 60198222..6f11c101 100644 --- a/testcases/veryl/28_msblsb.veryl +++ b/testcases/veryl/28_msblsb.veryl @@ -2,8 +2,8 @@ module Module28 { local WIDTH0: u32 = 10; local WIDTH1: u32 = 20; - var a: logic<10, 20> ; - var b: logic; + let a: logic<10, 20> = 1; + let b: logic = 1; let _x: logic = a[msb][msb:lsb + 1]; let _y: logic = b[msb - 3][msb + 5:lsb]; diff --git a/testcases/veryl/29_allow.veryl b/testcases/veryl/29_allow.veryl index 002eec44..cd2a851e 100644 --- a/testcases/veryl/29_allow.veryl +++ b/testcases/veryl/29_allow.veryl @@ -5,7 +5,7 @@ module Module29 ( var a: logic; var b: logic; #[allow(unused_variable)] - var c: logic; + let c: logic = 1; #[allow(missing_reset_statement)] always_ff (clk, rst) { diff --git a/testcases/veryl/35_unconnected_port.veryl b/testcases/veryl/35_unconnected_port.veryl index b6b63401..7cc9b077 100644 --- a/testcases/veryl/35_unconnected_port.veryl +++ b/testcases/veryl/35_unconnected_port.veryl @@ -1,5 +1,5 @@ module Module35 { - var aa: logic; + let aa: logic = 1; inst xx: Module35B ( aa , diff --git a/testcases/veryl/36_doc_comment.veryl b/testcases/veryl/36_doc_comment.veryl index da3b062a..514ea364 100644 --- a/testcases/veryl/36_doc_comment.veryl +++ b/testcases/veryl/36_doc_comment.veryl @@ -11,7 +11,9 @@ pub module Module36 #( i_rst_n: input logic , /// Reset i_data : input logic, /// Data input o_data : output logic, /// Data output -) {} +) { + assign o_data = 0; +} /// Test interface for doc comment /// diff --git a/testcases/veryl/45_let_in_always.veryl b/testcases/veryl/45_let_in_always.veryl index e71056fb..34f950ba 100644 --- a/testcases/veryl/45_let_in_always.veryl +++ b/testcases/veryl/45_let_in_always.veryl @@ -1,5 +1,5 @@ module Module45 { - var a: logic ; + let a: logic = 1; var b: logic<10>; var c: logic<10>; diff --git a/testcases/veryl/46_let_anywhere.veryl b/testcases/veryl/46_let_anywhere.veryl index 41b1921d..41efa842 100644 --- a/testcases/veryl/46_let_anywhere.veryl +++ b/testcases/veryl/46_let_anywhere.veryl @@ -1,5 +1,5 @@ module Module46 { - var a: logic ; + let a: logic = 1; var b: logic<10>; var c: logic<10>; var d: logic<10>; From 65727d629e6b46d853b33eed1fc46fa7a11ba730 Mon Sep 17 00:00:00 2001 From: dalance Date: Tue, 19 Mar 2024 11:28:33 +0900 Subject: [PATCH 2/9] Add UnassignVariable error to lsp error filtering --- crates/languageserver/src/server.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/languageserver/src/server.rs b/crates/languageserver/src/server.rs index 0cfc3d85..1dee995d 100644 --- a/crates/languageserver/src/server.rs +++ b/crates/languageserver/src/server.rs @@ -625,8 +625,8 @@ impl Server { } else { !matches!( x, - AnalyzerError::UndefinedIdentifier { .. } // After #573 merged - // | AnalyzerError::UnassignVariable { .. } + AnalyzerError::UndefinedIdentifier { .. } + | AnalyzerError::UnassignVariable { .. } ) } }) From 91133cc985fbc645dabfb55b54a1866d3c68ac74 Mon Sep 17 00:00:00 2001 From: dalance Date: Wed, 27 Mar 2024 12:42:44 +0900 Subject: [PATCH 3/9] Add multiple_assignment error support --- Cargo.lock | 14 +- crates/analyzer/Cargo.toml | 1 + crates/analyzer/src/analyzer.rs | 55 ++++- crates/analyzer/src/analyzer_error.rs | 22 +- .../analyzer/src/handlers/check_assignment.rs | 223 ++++++++++++++---- crates/analyzer/src/symbol_table.rs | 72 +++++- crates/analyzer/src/tests.rs | 52 ++-- crates/parser/src/veryl_token.rs | 4 +- testcases/sv/03_operator.sv | 125 ++++++---- testcases/sv/06_function.sv | 3 +- testcases/sv/13_range_operator.sv | 12 +- testcases/sv/18_concatenation.sv | 7 +- testcases/sv/25_dependency.sv | 7 +- testcases/veryl/03_operator.veryl | 87 +++---- testcases/veryl/06_function.veryl | 3 +- testcases/veryl/13_range_operator.veryl | 12 +- testcases/veryl/18_concatenation.veryl | 7 +- testcases/veryl/25_dependency.veryl | 19 +- 18 files changed, 505 insertions(+), 220 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bce297ec..cf500e4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -547,7 +547,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools", + "itertools 0.10.5", "num-traits", "once_cell", "oorandom", @@ -568,7 +568,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.5", ] [[package]] @@ -2259,6 +2259,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -4404,6 +4413,7 @@ dependencies = [ "Inflector", "bimap", "daggy", + "itertools 0.12.1", "miette", "strnum_bitwidth", "thiserror", diff --git a/crates/analyzer/Cargo.toml b/crates/analyzer/Cargo.toml index 8bf95d79..7bb0e5c4 100644 --- a/crates/analyzer/Cargo.toml +++ b/crates/analyzer/Cargo.toml @@ -14,6 +14,7 @@ edition = "2021" [dependencies] Inflector = "0.11.4" +itertools = "0.12.1" miette = {workspace = true} strnum_bitwidth = {workspace = true} thiserror = {workspace = true} diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index 952e390f..1f0a045c 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -2,9 +2,10 @@ use crate::analyzer_error::AnalyzerError; use crate::handlers::*; use crate::namespace_table; use crate::symbol::{ - Direction, ParameterValue, SymbolId, SymbolKind, TypeKind, VariableAffiniation, + Direction, ParameterValue, Symbol, SymbolId, SymbolKind, TypeKind, VariableAffiniation, }; -use crate::symbol_table::{self, AssignPath, ResolveSymbol}; +use crate::symbol_table::{self, AssignPath, AssignPosition, AssignPositionType, ResolveSymbol}; +use itertools::Itertools; use std::path::Path; use veryl_metadata::{Lint, Metadata}; use veryl_parser::resource_table; @@ -159,13 +160,13 @@ impl Analyzer { for assign in &assign_list { for assignable in &mut assignable_list { if assignable.0.included(&assign.path) { - assignable.1.push(assign.position.clone()); + assignable.1.push((assign.position.clone(), assign.partial)); } } } - for (path, position) in &assignable_list { - if position.is_empty() { + for (path, positions) in &assignable_list { + if positions.is_empty() { let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); ret.push(AnalyzerError::unassign_variable( &symbol.token.to_string(), @@ -173,6 +174,12 @@ impl Analyzer { &symbol.token, )); } + if positions.len() > 1 { + let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + for comb in positions.iter().combinations(2) { + ret.append(&mut check_assign_position(&symbol, text, comb[0], comb[1])); + } + } } ret @@ -333,3 +340,41 @@ fn traverse_assignable_symbol(id: SymbolId, path: &AssignPath) -> Vec Vec { + let x_pos = &x.0; + let y_pos = &y.0; + let x_partial = &x.1; + let y_partial = &y.1; + let mut ret = Vec::new(); + let len = x_pos.0.len().min(y_pos.0.len()); + + for i in 0..len { + let x_type = &x_pos.0[i]; + let y_type = &y_pos.0[i]; + if x_type != y_type { + match x_type { + AssignPositionType::DeclarationBranch { .. } + | AssignPositionType::Declaration { .. } => { + if !x_partial | !y_partial { + ret.push(AnalyzerError::multiple_assignment( + &symbol.token.to_string(), + text, + &symbol.token, + x_pos.0.last().unwrap().token(), + y_pos.0.last().unwrap().token(), + )); + } + } + _ => return vec![], + } + } + } + + ret +} diff --git a/crates/analyzer/src/analyzer_error.rs b/crates/analyzer/src/analyzer_error.rs index f5a19f7b..f759de51 100644 --- a/crates/analyzer/src/analyzer_error.rs +++ b/crates/analyzer/src/analyzer_error.rs @@ -37,17 +37,21 @@ pub enum AnalyzerError { #[diagnostic( severity(Error), - code(duplicated_assignment), + code(multiple_assignment), help(""), - url("https://doc.veryl-lang.org/book/06_appendix/02_semantic_error.html#duplicated_assignment") + url("https://doc.veryl-lang.org/book/06_appendix/02_semantic_error.html#multiple_assignment") )] #[error("{identifier} is assigned in multiple procedural blocks or assignment statements")] - DuplicatedAssignment { + MultipleAssignment { identifier: String, #[source_code] input: NamedSource, #[label("Error location")] error_location: SourceSpan, + #[label("Assigned")] + assign_pos0: SourceSpan, + #[label("Assigned too")] + assign_pos1: SourceSpan, }, #[diagnostic( @@ -516,11 +520,19 @@ impl AnalyzerError { } } - pub fn duplicated_assignment(identifier: &str, source: &str, token: &Token) -> Self { - AnalyzerError::DuplicatedAssignment { + pub fn multiple_assignment( + identifier: &str, + source: &str, + token: &Token, + assign_pos0: &Token, + assign_pos1: &Token, + ) -> Self { + AnalyzerError::MultipleAssignment { identifier: identifier.to_string(), input: AnalyzerError::named_source(source, token), error_location: token.into(), + assign_pos0: assign_pos0.into(), + assign_pos1: assign_pos1.into(), } } diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index da8ca423..e2362650 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -1,6 +1,9 @@ use crate::analyzer_error::AnalyzerError; use crate::symbol::{Direction, SymbolId, SymbolKind}; -use crate::symbol_table::{self, AssignPosition, AssignPositionType, ResolveSymbol}; +use crate::symbol_table::{ + self, AssignDeclarationType, AssignPosition, AssignPositionType, AssignStatementBranchType, + ResolveSymbol, +}; use std::collections::HashMap; use veryl_parser::veryl_grammar_trait::*; use veryl_parser::veryl_walker::{Handler, HandlerPoint}; @@ -12,6 +15,7 @@ pub struct CheckAssignment<'a> { point: HandlerPoint, assign_position: AssignPosition, in_if_expression: Vec<()>, + branch_index: usize, } impl<'a> CheckAssignment<'a> { @@ -22,6 +26,7 @@ impl<'a> CheckAssignment<'a> { point: HandlerPoint::Before, assign_position: AssignPosition::default(), in_if_expression: Vec::new(), + branch_index: 0, } } } @@ -75,14 +80,21 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn r#else(&mut self, arg: &Else) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if self.in_if_expression.is_empty() { - let new_position = if let AssignPositionType::StatementBlock(_) = + let new_position = if let AssignPositionType::StatementBranchItem { .. } = self.assign_position.0.last().unwrap() { - AssignPositionType::StatementBlock(arg.else_token.token) + AssignPositionType::StatementBranchItem { + token: arg.else_token.token, + index: self.branch_index, + } } else { - AssignPositionType::DeclarationBlock(arg.else_token.token) + AssignPositionType::DeclarationBranchItem { + token: arg.else_token.token, + index: self.branch_index, + } }; *self.assign_position.0.last_mut().unwrap() = new_position; + self.branch_index += 1; } } Ok(()) @@ -103,9 +115,11 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn let_statement(&mut self, arg: &LetStatement) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Statement(arg.r#let.let_token.token)); - symbol_table::add_assign(x.full_path, position); + self.assign_position.push(AssignPositionType::Statement { + token: arg.equ.equ_token.token, + }); + symbol_table::add_assign(x.full_path, &self.assign_position, false); + self.assign_position.pop(); } } Ok(()) @@ -113,13 +127,41 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn identifier_statement(&mut self, arg: &IdentifierStatement) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { - if let IdentifierStatementGroup::Assignment(_) = &*arg.identifier_statement_group { + if let IdentifierStatementGroup::Assignment(x) = &*arg.identifier_statement_group { + let token = match x.assignment.assignment_group.as_ref() { + AssignmentGroup::Equ(x) => x.equ.equ_token.token, + AssignmentGroup::AssignmentOperator(x) => { + x.assignment_operator.assignment_operator_token.token + } + }; if let Ok(x) = symbol_table::resolve(arg.expression_identifier.as_ref()) { let full_path = x.full_path; match x.found { ResolveSymbol::Symbol(x) => { if can_assign(&full_path) { - symbol_table::add_assign(full_path, self.assign_position.clone()); + let partial = match arg + .expression_identifier + .expression_identifier_group + .as_ref() + { + ExpressionIdentifierGroup::ExpressionIdentifierScoped(x) => !x + .expression_identifier_scoped + .expression_identifier_scoped_list0 + .is_empty(), + ExpressionIdentifierGroup::ExpressionIdentifierMember(x) => { + let x = &x.expression_identifier_member; + !x.expression_identifier_member_list.is_empty() + | x.expression_identifier_member_list0.iter().any(|x| { + !x.expression_identifier_member_list0_list + .is_empty() + }) + } + }; + + self.assign_position + .push(AssignPositionType::Statement { token }); + symbol_table::add_assign(full_path, &self.assign_position, partial); + self.assign_position.pop(); } else { let token = &arg.expression_identifier.identifier.identifier_token.token; @@ -143,11 +185,24 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn if_statement(&mut self, arg: &IfStatement) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { + self.branch_index = 0; + let branches = 1 + arg.if_statement_list0.len() + arg.if_statement_opt.iter().len(); self.assign_position - .push(AssignPositionType::StatementBlock(arg.r#if.if_token.token)); + .push(AssignPositionType::StatementBranch { + token: arg.r#if.if_token.token, + branches, + r#type: AssignStatementBranchType::If, + }); + self.assign_position + .push(AssignPositionType::StatementBranchItem { + token: arg.r#if.if_token.token, + index: self.branch_index, + }); + self.branch_index += 1; } HandlerPoint::After => { self.assign_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -156,13 +211,26 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn if_reset_statement(&mut self, arg: &IfResetStatement) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { + self.branch_index = 0; + let branches = 1 + + arg.if_reset_statement_list0.len() + + arg.if_reset_statement_opt.iter().len(); self.assign_position - .push(AssignPositionType::StatementBlock( - arg.if_reset.if_reset_token.token, - )); + .push(AssignPositionType::StatementBranch { + token: arg.if_reset.if_reset_token.token, + branches, + r#type: AssignStatementBranchType::IfReset, + }); + self.assign_position + .push(AssignPositionType::StatementBranchItem { + token: arg.if_reset.if_reset_token.token, + index: self.branch_index, + }); + self.branch_index += 1; } HandlerPoint::After => { self.assign_position.pop(); + self.assign_position.pop(); } } Ok(()) @@ -171,9 +239,30 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn for_statement(&mut self, arg: &ForStatement) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Statement(arg.r#for.for_token.token)); - symbol_table::add_assign(x.full_path, position); + self.assign_position.push(AssignPositionType::Statement { + token: arg.r#for.for_token.token, + }); + symbol_table::add_assign(x.full_path, &self.assign_position, false); + self.assign_position.pop(); + } + } + Ok(()) + } + + fn case_statement(&mut self, arg: &CaseStatement) -> Result<(), ParolError> { + match self.point { + HandlerPoint::Before => { + self.branch_index = 0; + let branches = arg.case_statement_list.len(); + self.assign_position + .push(AssignPositionType::StatementBranch { + token: arg.case.case_token.token, + branches, + r#type: AssignStatementBranchType::Case, + }); + } + HandlerPoint::After => { + self.assign_position.pop(); } } Ok(()) @@ -183,9 +272,11 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { match self.point { HandlerPoint::Before => { self.assign_position - .push(AssignPositionType::StatementBlock( - arg.colon.colon_token.token, - )); + .push(AssignPositionType::StatementBranchItem { + token: arg.colon.colon_token.token, + index: self.branch_index, + }); + self.branch_index += 1; } HandlerPoint::After => { self.assign_position.pop(); @@ -197,9 +288,12 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn let_declaration(&mut self, arg: &LetDeclaration) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Declaration(arg.r#let.let_token.token)); - symbol_table::add_assign(x.full_path, position); + self.assign_position.push(AssignPositionType::Declaration { + token: arg.r#let.let_token.token, + r#type: AssignDeclarationType::Let, + }); + symbol_table::add_assign(x.full_path, &self.assign_position, false); + self.assign_position.pop(); } } Ok(()) @@ -208,9 +302,10 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn always_ff_declaration(&mut self, arg: &AlwaysFfDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.assign_position.push(AssignPositionType::Declaration( - arg.always_ff.always_ff_token.token, - )); + self.assign_position.push(AssignPositionType::Declaration { + token: arg.always_ff.always_ff_token.token, + r#type: AssignDeclarationType::AlwaysFf, + }); } HandlerPoint::After => { self.assign_position.pop(); @@ -222,9 +317,10 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn always_comb_declaration(&mut self, arg: &AlwaysCombDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.assign_position.push(AssignPositionType::Declaration( - arg.always_comb.always_comb_token.token, - )); + self.assign_position.push(AssignPositionType::Declaration { + token: arg.always_comb.always_comb_token.token, + r#type: AssignDeclarationType::AlwaysComb, + }); } HandlerPoint::After => { self.assign_position.pop(); @@ -240,11 +336,23 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { match x.found { ResolveSymbol::Symbol(x) => { if can_assign(&full_path) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Declaration( - arg.assign.assign_token.token, - )); - symbol_table::add_assign(full_path, position); + // selected partially + let partial = !arg + .hierarchical_identifier + .hierarchical_identifier_list + .is_empty() + | arg + .hierarchical_identifier + .hierarchical_identifier_list0 + .iter() + .any(|x| !x.hierarchical_identifier_list0_list.is_empty()); + + self.assign_position.push(AssignPositionType::Declaration { + token: arg.assign.assign_token.token, + r#type: AssignDeclarationType::Assign, + }); + symbol_table::add_assign(full_path, &self.assign_position, partial); + self.assign_position.pop(); } else { let token = &arg .hierarchical_identifier @@ -304,11 +412,18 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { if let Ok(x) = symbol_table::resolve((target, &symbol.namespace)) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Declaration( - arg.inst.inst_token.token, - )); - symbol_table::add_assign(x.full_path, position); + self.assign_position.push( + AssignPositionType::Declaration { + token: arg.inst.inst_token.token, + r#type: AssignDeclarationType::Inst, + }, + ); + symbol_table::add_assign( + x.full_path, + &self.assign_position, + false, + ); + self.assign_position.pop(); } } } @@ -323,9 +438,10 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn function_declaration(&mut self, arg: &FunctionDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { - self.assign_position.push(AssignPositionType::Declaration( - arg.function.function_token.token, - )); + self.assign_position.push(AssignPositionType::Declaration { + token: arg.function.function_token.token, + r#type: AssignDeclarationType::Function, + }); } HandlerPoint::After => { self.assign_position.pop(); @@ -337,10 +453,21 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn module_if_declaration(&mut self, arg: &ModuleIfDeclaration) -> Result<(), ParolError> { match self.point { HandlerPoint::Before => { + self.branch_index = 0; + let branches = 1 + + arg.module_if_declaration_list.len() + + arg.module_if_declaration_opt.iter().len(); + self.assign_position + .push(AssignPositionType::DeclarationBranch { + token: arg.r#if.if_token.token, + branches, + }); self.assign_position - .push(AssignPositionType::DeclarationBlock( - arg.r#if.if_token.token, - )); + .push(AssignPositionType::DeclarationBranchItem { + token: arg.r#if.if_token.token, + index: self.branch_index, + }); + self.branch_index += 1; } HandlerPoint::After => { self.assign_position.pop(); @@ -352,9 +479,11 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { fn module_for_declaration(&mut self, arg: &ModuleForDeclaration) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { - let mut position = self.assign_position.clone(); - position.push(AssignPositionType::Statement(arg.r#for.for_token.token)); - symbol_table::add_assign(x.full_path, position); + self.assign_position.push(AssignPositionType::Statement { + token: arg.r#for.for_token.token, + }); + symbol_table::add_assign(x.full_path, &self.assign_position, false); + self.assign_position.pop(); } } Ok(()) diff --git a/crates/analyzer/src/symbol_table.rs b/crates/analyzer/src/symbol_table.rs index a0d36e68..b598dd21 100644 --- a/crates/analyzer/src/symbol_table.rs +++ b/crates/analyzer/src/symbol_table.rs @@ -234,6 +234,7 @@ impl ResolveError { pub struct Assign { pub path: AssignPath, pub position: AssignPosition, + pub partial: bool, } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -311,25 +312,64 @@ impl fmt::Display for AssignPosition { } } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum AssignPositionType { - DeclarationBlock(Token), - Declaration(Token), - StatementBlock(Token), - Statement(Token), + DeclarationBranch { + token: Token, + branches: usize, + }, + DeclarationBranchItem { + token: Token, + index: usize, + }, + Declaration { + token: Token, + r#type: AssignDeclarationType, + }, + StatementBranch { + token: Token, + branches: usize, + r#type: AssignStatementBranchType, + }, + StatementBranchItem { + token: Token, + index: usize, + }, + Statement { + token: Token, + }, } impl AssignPositionType { pub fn token(&self) -> &Token { match self { - AssignPositionType::DeclarationBlock(x) => x, - AssignPositionType::Declaration(x) => x, - AssignPositionType::StatementBlock(x) => x, - AssignPositionType::Statement(x) => x, + AssignPositionType::DeclarationBranch { token: x, .. } => x, + AssignPositionType::DeclarationBranchItem { token: x, .. } => x, + AssignPositionType::Declaration { token: x, .. } => x, + AssignPositionType::StatementBranch { token: x, .. } => x, + AssignPositionType::StatementBranchItem { token: x, .. } => x, + AssignPositionType::Statement { token: x, .. } => x, } } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignDeclarationType { + Let, + AlwaysFf, + AlwaysComb, + Assign, + Inst, + Function, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignStatementBranchType { + If, + IfReset, + Case, +} + #[derive(Clone, Default, Debug)] pub struct SymbolTable { name_table: HashMap>, @@ -690,10 +730,16 @@ impl SymbolTable { self.project_local_table.get(&prj).cloned() } - pub fn add_assign(&mut self, full_path: Vec, position: AssignPosition) { + pub fn add_assign( + &mut self, + full_path: Vec, + position: &AssignPosition, + partial: bool, + ) { let assign = Assign { path: AssignPath(full_path), - position, + position: position.clone(), + partial, }; self.assign_list.push(assign); } @@ -1007,8 +1053,8 @@ pub fn get_project_local(prj: StrId) -> Option> { SYMBOL_TABLE.with(|f| f.borrow().get_project_local(prj)) } -pub fn add_assign(full_path: Vec, position: AssignPosition) { - SYMBOL_TABLE.with(|f| f.borrow_mut().add_assign(full_path, position)) +pub fn add_assign(full_path: Vec, position: &AssignPosition, partial: bool) { + SYMBOL_TABLE.with(|f| f.borrow_mut().add_assign(full_path, position, partial)) } pub fn get_assign_list() -> Vec { diff --git a/crates/analyzer/src/tests.rs b/crates/analyzer/src/tests.rs index 958fba4c..ce2940dc 100644 --- a/crates/analyzer/src/tests.rs +++ b/crates/analyzer/src/tests.rs @@ -76,28 +76,28 @@ fn duplicated_identifier() { )); } -//#[test] -//fn duplicated_assignment() { -// let code = r#" -// module ModuleA { -// var a: logic; -// -// assign a = 1; -// always_comb { -// a = 1; -// } -// } -// "#; -// -// let errors = analyze(code); -// assert!(matches!( -// errors[0], -// AnalyzerError::DuplicatedAssignment { .. } -// )); -//} +#[test] +fn multiple_assignment() { + let code = r#" + module ModuleA { + var a: logic; + + assign a = 1; + always_comb { + a = 1; + } + } + "#; + + let errors = analyze(code); + assert!(matches!( + errors[0], + AnalyzerError::MultipleAssignment { .. } + )); +} #[test] -fn duplicated_assignment() { +fn invalid_allow() { let code = r#" module ModuleA { #[allow(dummy_name)] @@ -503,3 +503,15 @@ fn unused_return() { let errors = analyze(code); assert!(matches!(errors[0], AnalyzerError::UnusedReturn { .. })); } + +#[test] +fn unassign_variable() { + let code = r#" + module ModuleA { + var _a: logic; + } + "#; + + let errors = analyze(code); + assert!(matches!(errors[0], AnalyzerError::UnassignVariable { .. })); +} diff --git a/crates/parser/src/veryl_token.rs b/crates/parser/src/veryl_token.rs index daba6d42..fde2e14c 100644 --- a/crates/parser/src/veryl_token.rs +++ b/crates/parser/src/veryl_token.rs @@ -6,7 +6,7 @@ use paste::paste; use regex::Regex; use std::fmt; -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TokenSource { File(PathId), Builtin, @@ -33,7 +33,7 @@ impl PartialEq for TokenSource { } } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Token { pub id: TokenId, pub text: StrId, diff --git a/testcases/sv/03_operator.sv b/testcases/sv/03_operator.sv index 70b6dee1..7de94c50 100644 --- a/testcases/sv/03_operator.sv +++ b/testcases/sv/03_operator.sv @@ -1,66 +1,93 @@ module veryl_testcase_Module03; - logic a ; - logic aa ; - logic aaa ; - logic aaaa ; - logic aaaaa ; - logic aaaaaa ; - logic aaaaaaa ; - logic aaaaaaaa ; - logic aaaaaaaaa ; - logic aaaaaaaaaa; - // unary arithmetic - always_comb a = +1; - always_comb aa = -1; + logic _a ; + always_comb _a = +1; + logic _aa; + always_comb _aa = -1; // unary logical - always_comb a = !1; - always_comb aa = ~1; + logic _b ; + always_comb _b = !1; + logic _bb; + always_comb _bb = ~1; // unary reduce - always_comb a = &1; - always_comb aa = |1; - always_comb aaa = ^1; - always_comb aaaa = ~&1; - always_comb aaaaa = ~|1; - always_comb aaaaaa = ~^1; - always_comb aaaaaaa = ^~1; + logic _c ; + always_comb _c = &1; + logic _cc ; + always_comb _cc = |1; + logic _ccc ; + always_comb _ccc = ^1; + logic _cccc ; + always_comb _cccc = ~&1; + logic _ccccc ; + always_comb _ccccc = ~|1; + logic _cccccc ; + always_comb _cccccc = ~^1; + logic _ccccccc; + always_comb _ccccccc = ^~1; // binary arithmetic - always_comb a = 1 ** 1; - always_comb aa = 1 * 1; - always_comb aaa = 1 / 1; - always_comb aaaa = 1 % 1; - always_comb aaaaa = 1 + 1; - always_comb aaaaaa = 1 - 1; + logic _d ; + always_comb _d = 1 ** 1; + logic _dd ; + always_comb _dd = 1 * 1; + logic _ddd ; + always_comb _ddd = 1 / 1; + logic _dddd ; + always_comb _dddd = 1 % 1; + logic _ddddd ; + always_comb _ddddd = 1 + 1; + logic _dddddd; + always_comb _dddddd = 1 - 1; // binary shift - always_comb a = 1 << 1; - always_comb aa = 1 >> 1; - always_comb aaa = 1 <<< 1; - always_comb aaaa = 1 >>> 1; + logic _e ; + always_comb _e = 1 << 1; + logic _ee ; + always_comb _ee = 1 >> 1; + logic _eee ; + always_comb _eee = 1 <<< 1; + logic _eeee; + always_comb _eeee = 1 >>> 1; // binary compare - always_comb a = 1 < 1; - always_comb aa = 1 <= 1; - always_comb aaa = 1 > 1; - always_comb aaaa = 1 >= 1; - always_comb aaaaa = 1 == 1; - always_comb aaaaaa = 1 != 1; - always_comb aaaaaaa = 1 === 1; - always_comb aaaaaaaa = 1 !== 1; - always_comb aaaaaaaaa = 1 ==? 1; - always_comb aaaaaaaaaa = 1 !=? 1; + logic _f ; + always_comb _f = 1 < 1; + logic _ff ; + always_comb _ff = 1 <= 1; + logic _fff ; + always_comb _fff = 1 > 1; + logic _ffff ; + always_comb _ffff = 1 >= 1; + logic _fffff ; + always_comb _fffff = 1 == 1; + logic _ffffff ; + always_comb _ffffff = 1 != 1; + logic _fffffff ; + always_comb _fffffff = 1 === 1; + logic _ffffffff ; + always_comb _ffffffff = 1 !== 1; + logic _fffffffff ; + always_comb _fffffffff = 1 ==? 1; + logic _ffffffffff; + always_comb _ffffffffff = 1 !=? 1; // binary bitwise - always_comb a = 1 & 1; - always_comb aa = 1 ^ 1; - always_comb aaa = 1 ~^ 1; - always_comb aaaa = 1 ^~ 1; - always_comb aaaaa = 1 | 1; + logic _g ; + always_comb _g = 1 & 1; + logic _gg ; + always_comb _gg = 1 ^ 1; + logic _ggg ; + always_comb _ggg = 1 ~^ 1; + logic _gggg ; + always_comb _gggg = 1 ^~ 1; + logic _ggggg; + always_comb _ggggg = 1 | 1; // binary logical - always_comb a = 1 && 1; - always_comb aa = 1 || 1; + logic _h ; + always_comb _h = 1 && 1; + logic _hh; + always_comb _hh = 1 || 1; endmodule diff --git a/testcases/sv/06_function.sv b/testcases/sv/06_function.sv index a3efdea8..d473035c 100644 --- a/testcases/sv/06_function.sv +++ b/testcases/sv/06_function.sv @@ -44,6 +44,7 @@ module veryl_testcase_Module06; logic [ParamX-1:0] c; always_comb c = 1; logic [ParamX-1:0] d; + logic [ParamX-1:0] e; // function call always_comb d = FuncA(a, b, c); @@ -57,5 +58,5 @@ module veryl_testcase_Module06; end // system function call - always_comb d = $clog2(a); + always_comb e = $clog2(a); endmodule diff --git a/testcases/sv/13_range_operator.sv b/testcases/sv/13_range_operator.sv index 021ac39b..1d65cd54 100644 --- a/testcases/sv/13_range_operator.sv +++ b/testcases/sv/13_range_operator.sv @@ -1,5 +1,9 @@ module veryl_testcase_Module13; logic a; + logic b; + logic c; + logic d; + logic e; logic X; always_comb X = 1; @@ -7,12 +11,12 @@ module veryl_testcase_Module13; always_comb a = X[0]; // range select - always_comb a = X[1:0]; + always_comb b = X[1:0]; // position and width - always_comb a = X[1+:2]; - always_comb a = X[1-:2]; + always_comb c = X[1+:2]; + always_comb d = X[1-:2]; // index by step - always_comb a = X[1*(2)+:(2)]; + always_comb e = X[1*(2)+:(2)]; endmodule diff --git a/testcases/sv/18_concatenation.sv b/testcases/sv/18_concatenation.sv index 7a8527f4..70c026a6 100644 --- a/testcases/sv/18_concatenation.sv +++ b/testcases/sv/18_concatenation.sv @@ -1,8 +1,9 @@ module veryl_testcase_Module18; logic a; logic b; - always_comb b = 1; + logic c; + always_comb c = 1; - always_comb a = {a[10:0], b}; - always_comb a = {{10{a[10:0]}}, {4{b}}}; + always_comb a = {a[10:0], c}; + always_comb b = {{10{a[10:0]}}, {4{c}}}; endmodule diff --git a/testcases/sv/25_dependency.sv b/testcases/sv/25_dependency.sv index b08d228f..b3df2662 100644 --- a/testcases/sv/25_dependency.sv +++ b/testcases/sv/25_dependency.sv @@ -2,19 +2,20 @@ module veryl_testcase_Module25 ( input logic i_clk , input logic i_rst_n, input logic i_d , - output logic o_d + output logic o_d0 , + output logic o_d1 ); veryl_sample1_delay u0 ( .i_clk (i_clk ), .i_rst_n (i_rst_n), .i_d (i_d ), - .o_d (o_d ) + .o_d (o_d0 ) ); veryl_sample2_delay u1 ( .i_clk (i_clk ), .i_rst_n (i_rst_n), .i_d (i_d ), - .o_d (o_d ) + .o_d (o_d1 ) ); endmodule diff --git a/testcases/veryl/03_operator.veryl b/testcases/veryl/03_operator.veryl index 80000252..e73b6c95 100644 --- a/testcases/veryl/03_operator.veryl +++ b/testcases/veryl/03_operator.veryl @@ -1,66 +1,55 @@ module Module03 { - var a : logic; - var aa : logic; - var aaa : logic; - var aaaa : logic; - var aaaaa : logic; - var aaaaaa : logic; - var aaaaaaa : logic; - var aaaaaaaa : logic; - var aaaaaaaaa : logic; - var aaaaaaaaaa: logic; - // unary arithmetic - assign a = +1; - assign aa = -1; + let _a : logic = +1; + let _aa: logic = -1; // unary logical - assign a = !1; - assign aa = ~1; + let _b : logic = !1; + let _bb: logic = ~1; // unary reduce - assign a = &1; - assign aa = |1; - assign aaa = ^1; - assign aaaa = ~&1; - assign aaaaa = ~|1; - assign aaaaaa = ~^1; - assign aaaaaaa = ^~1; + let _c : logic = &1; + let _cc : logic = |1; + let _ccc : logic = ^1; + let _cccc : logic = ~&1; + let _ccccc : logic = ~|1; + let _cccccc : logic = ~^1; + let _ccccccc: logic = ^~1; // binary arithmetic - assign a = 1 ** 1; - assign aa = 1 * 1; - assign aaa = 1 / 1; - assign aaaa = 1 % 1; - assign aaaaa = 1 + 1; - assign aaaaaa = 1 - 1; + let _d : logic = 1 ** 1; + let _dd : logic = 1 * 1; + let _ddd : logic = 1 / 1; + let _dddd : logic = 1 % 1; + let _ddddd : logic = 1 + 1; + let _dddddd: logic = 1 - 1; // binary shift - assign a = 1 << 1; - assign aa = 1 >> 1; - assign aaa = 1 <<< 1; - assign aaaa = 1 >>> 1; + let _e : logic = 1 << 1; + let _ee : logic = 1 >> 1; + let _eee : logic = 1 <<< 1; + let _eeee: logic = 1 >>> 1; // binary compare - assign a = 1 <: 1; - assign aa = 1 <= 1; - assign aaa = 1 >: 1; - assign aaaa = 1 >= 1; - assign aaaaa = 1 == 1; - assign aaaaaa = 1 != 1; - assign aaaaaaa = 1 === 1; - assign aaaaaaaa = 1 !== 1; - assign aaaaaaaaa = 1 ==? 1; - assign aaaaaaaaaa = 1 !=? 1; + let _f : logic = 1 <: 1; + let _ff : logic = 1 <= 1; + let _fff : logic = 1 >: 1; + let _ffff : logic = 1 >= 1; + let _fffff : logic = 1 == 1; + let _ffffff : logic = 1 != 1; + let _fffffff : logic = 1 === 1; + let _ffffffff : logic = 1 !== 1; + let _fffffffff : logic = 1 ==? 1; + let _ffffffffff: logic = 1 !=? 1; // binary bitwise - assign a = 1 & 1; - assign aa = 1 ^ 1; - assign aaa = 1 ~^ 1; - assign aaaa = 1 ^~ 1; - assign aaaaa = 1 | 1; + let _g : logic = 1 & 1; + let _gg : logic = 1 ^ 1; + let _ggg : logic = 1 ~^ 1; + let _gggg : logic = 1 ^~ 1; + let _ggggg: logic = 1 | 1; // binary logical - assign a = 1 && 1; - assign aa = 1 || 1; + let _h : logic = 1 && 1; + let _hh: logic = 1 || 1; } diff --git a/testcases/veryl/06_function.veryl b/testcases/veryl/06_function.veryl index dbf7d4a8..b1db646e 100644 --- a/testcases/veryl/06_function.veryl +++ b/testcases/veryl/06_function.veryl @@ -38,6 +38,7 @@ module Module06 { let b: logic = 1; let c: logic = 1; var d: logic; + var e: logic; // function call assign d = FuncA(a, b, c); @@ -51,5 +52,5 @@ module Module06 { } // system function call - assign d = $clog2(a); + assign e = $clog2(a); } diff --git a/testcases/veryl/13_range_operator.veryl b/testcases/veryl/13_range_operator.veryl index 00a63d6a..56d55489 100644 --- a/testcases/veryl/13_range_operator.veryl +++ b/testcases/veryl/13_range_operator.veryl @@ -1,17 +1,21 @@ module Module13 { var a: logic; + var b: logic; + var c: logic; + var d: logic; + var e: logic; let X: logic = 1; // bit select assign a = X[0]; // range select - assign a = X[1:0]; + assign b = X[1:0]; // position and width - assign a = X[1+:2]; - assign a = X[1-:2]; + assign c = X[1+:2]; + assign d = X[1-:2]; // index by step - assign a = X[1 step 2]; + assign e = X[1 step 2]; } diff --git a/testcases/veryl/18_concatenation.veryl b/testcases/veryl/18_concatenation.veryl index 4540cdd4..8295c3e4 100644 --- a/testcases/veryl/18_concatenation.veryl +++ b/testcases/veryl/18_concatenation.veryl @@ -1,7 +1,8 @@ module Module18 { var a: logic; - let b: logic = 1; + var b: logic; + let c: logic = 1; - assign a = {a[10:0], b}; - assign a = {a[10:0] repeat 10, b repeat 4}; + assign a = {a[10:0], c}; + assign b = {a[10:0] repeat 10, c repeat 4}; } diff --git a/testcases/veryl/25_dependency.veryl b/testcases/veryl/25_dependency.veryl index 609512f0..25ef9830 100644 --- a/testcases/veryl/25_dependency.veryl +++ b/testcases/veryl/25_dependency.veryl @@ -2,19 +2,20 @@ module Module25 ( i_clk : input logic, i_rst_n: input logic, i_d : input logic, - o_d : output logic, + o_d0 : output logic, + o_d1 : output logic, ) { inst u0: veryl_sample1::delay ( - i_clk , - i_rst_n , - i_d , - o_d , + i_clk , + i_rst_n , + i_d , + o_d : o_d0, ); inst u1: veryl_sample2::delay ( - i_clk , - i_rst_n , - i_d , - o_d , + i_clk , + i_rst_n , + i_d , + o_d : o_d1, ); } From 93f568229ea821bac8a085435643ce58b220ef97 Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 15:52:44 +0900 Subject: [PATCH 4/9] Add uncovered_branch error --- crates/analyzer/src/analyzer.rs | 42 +++++++++- crates/analyzer/src/analyzer_error.rs | 31 +++++++ .../analyzer/src/handlers/check_assignment.rs | 11 +++ crates/analyzer/src/symbol_table.rs | 82 +++++++++++++++++++ crates/analyzer/src/tests.rs | 19 +++++ 5 files changed, 181 insertions(+), 4 deletions(-) diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index 1f0a045c..691030ac 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -4,7 +4,9 @@ use crate::namespace_table; use crate::symbol::{ Direction, ParameterValue, Symbol, SymbolId, SymbolKind, TypeKind, VariableAffiniation, }; -use crate::symbol_table::{self, AssignPath, AssignPosition, AssignPositionType, ResolveSymbol}; +use crate::symbol_table::{ + self, AssignPath, AssignPosition, AssignPositionTree, AssignPositionType, ResolveSymbol, +}; use itertools::Itertools; use std::path::Path; use veryl_metadata::{Lint, Metadata}; @@ -174,12 +176,18 @@ impl Analyzer { &symbol.token, )); } + + let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + if positions.len() > 1 { - let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); for comb in positions.iter().combinations(2) { - ret.append(&mut check_assign_position(&symbol, text, comb[0], comb[1])); + ret.append(&mut check_multiple_assignment( + &symbol, text, comb[0], comb[1], + )); } } + + ret.append(&mut check_uncovered_branch(&symbol, text, positions)); } ret @@ -341,7 +349,7 @@ fn traverse_assignable_symbol(id: SymbolId, path: &AssignPath) -> Vec Vec { + let mut ret = Vec::new(); + + let mut tree = AssignPositionTree::default(); + for x in positions { + let pos = &x.0; + + tree.add(pos.clone()); + } + + if let Some(token) = tree.check_always_comb_uncovered() { + ret.push(AnalyzerError::uncovered_branch( + &symbol.token.to_string(), + text, + &symbol.token, + &token, + )); + } + + ret +} diff --git a/crates/analyzer/src/analyzer_error.rs b/crates/analyzer/src/analyzer_error.rs index f759de51..b797fe50 100644 --- a/crates/analyzer/src/analyzer_error.rs +++ b/crates/analyzer/src/analyzer_error.rs @@ -496,6 +496,23 @@ pub enum AnalyzerError { #[label("Error location")] error_location: SourceSpan, }, + + #[diagnostic( + severity(Warning), + code(uncovered_branch), + help(""), + url("https://doc.veryl-lang.org/book/06_appendix/02_semantic_error.html#uncovered_branch") + )] + #[error("{identifier} is not covered by all branches, it causes latch generation")] + UncoveredBranch { + identifier: String, + #[source_code] + input: NamedSource, + #[label("Error location")] + error_location: SourceSpan, + #[label("Uncovered")] + uncovered: SourceSpan, + }, } impl AnalyzerError { @@ -797,4 +814,18 @@ impl AnalyzerError { error_location: token.into(), } } + + pub fn uncovered_branch( + identifier: &str, + source: &str, + token: &Token, + uncovered: &Token, + ) -> Self { + AnalyzerError::UncoveredBranch { + identifier: identifier.to_string(), + input: AnalyzerError::named_source(source, token), + error_location: token.into(), + uncovered: uncovered.into(), + } + } } diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index e2362650..4644df95 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -187,10 +187,12 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { HandlerPoint::Before => { self.branch_index = 0; let branches = 1 + arg.if_statement_list0.len() + arg.if_statement_opt.iter().len(); + let has_default = arg.if_statement_opt.is_some(); self.assign_position .push(AssignPositionType::StatementBranch { token: arg.r#if.if_token.token, branches, + has_default, r#type: AssignStatementBranchType::If, }); self.assign_position @@ -215,10 +217,12 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { let branches = 1 + arg.if_reset_statement_list0.len() + arg.if_reset_statement_opt.iter().len(); + let has_default = arg.if_reset_statement_opt.is_some(); self.assign_position .push(AssignPositionType::StatementBranch { token: arg.if_reset.if_reset_token.token, branches, + has_default, r#type: AssignStatementBranchType::IfReset, }); self.assign_position @@ -254,10 +258,17 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { HandlerPoint::Before => { self.branch_index = 0; let branches = arg.case_statement_list.len(); + let has_default = arg.case_statement_list.iter().any(|x| { + matches!( + x.case_item.case_item_group.as_ref(), + CaseItemGroup::Defaul(_) + ) + }); self.assign_position .push(AssignPositionType::StatementBranch { token: arg.case.case_token.token, branches, + has_default, r#type: AssignStatementBranchType::Case, }); } diff --git a/crates/analyzer/src/symbol_table.rs b/crates/analyzer/src/symbol_table.rs index b598dd21..f22ea72f 100644 --- a/crates/analyzer/src/symbol_table.rs +++ b/crates/analyzer/src/symbol_table.rs @@ -329,6 +329,7 @@ pub enum AssignPositionType { StatementBranch { token: Token, branches: usize, + has_default: bool, r#type: AssignStatementBranchType, }, StatementBranchItem { @@ -370,6 +371,87 @@ pub enum AssignStatementBranchType { Case, } +#[derive(Clone, Default, Debug)] +pub struct AssignPositionTree { + r#type: Option, + children: Vec, +} + +impl AssignPositionTree { + pub fn add(&mut self, mut pos: AssignPosition) { + if pos.0.is_empty() { + return; + } + + let mut head: Vec<_> = pos.0.drain(0..1).collect(); + + for child in &mut self.children { + if child.r#type.as_ref() == head.first() { + child.add(pos); + return; + } + } + + let mut node = AssignPositionTree { + r#type: Some(head.remove(0)), + children: vec![], + }; + node.add(pos); + self.children.push(node); + } + + pub fn check_always_comb_uncovered(&self) -> Option { + if let Some(AssignPositionType::Declaration { ref r#type, .. }) = self.r#type { + if *r#type == AssignDeclarationType::AlwaysComb { + return self + .children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten(); + } + } + + for child in &self.children { + let ret = child.check_always_comb_uncovered(); + if ret.is_some() { + return ret; + } + } + + None + } + + fn impl_always_comb_uncovered(&self) -> Option { + match self.r#type { + Some(AssignPositionType::StatementBranch { + token, + branches, + has_default, + .. + }) => { + if !has_default || self.children.len() != branches { + Some(token) + } else { + self.children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten() + } + } + Some(AssignPositionType::StatementBranchItem { .. }) => self + .children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten(), + Some(AssignPositionType::Statement { .. }) => None, + _ => unreachable!(), + } + } +} + #[derive(Clone, Default, Debug)] pub struct SymbolTable { name_table: HashMap>, diff --git a/crates/analyzer/src/tests.rs b/crates/analyzer/src/tests.rs index ce2940dc..90a61875 100644 --- a/crates/analyzer/src/tests.rs +++ b/crates/analyzer/src/tests.rs @@ -515,3 +515,22 @@ fn unassign_variable() { let errors = analyze(code); assert!(matches!(errors[0], AnalyzerError::UnassignVariable { .. })); } + +#[test] +fn uncovered_branch() { + let code = r#" + module ModuleA { + var a: logic; + let x: logic = 1; + + always_comb { + if x { + a = 1; + } + } + } + "#; + + let errors = analyze(code); + assert!(matches!(errors[0], AnalyzerError::UncoveredBranch { .. })); +} From 0b5ff7661868a54d9b244b1b3a46d853c3b5416e Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 17:03:13 +0900 Subject: [PATCH 5/9] Move if_reset assign check to analyzer pass3 --- crates/analyzer/src/analyzer.rs | 20 +++++- crates/analyzer/src/analyzer_error.rs | 5 +- .../analyzer/src/handlers/check_assignment.rs | 23 ++++-- crates/analyzer/src/handlers/check_reset.rs | 70 +------------------ crates/analyzer/src/symbol_table.rs | 51 ++++++++++++++ 5 files changed, 93 insertions(+), 76 deletions(-) diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index 691030ac..36ede07e 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -170,8 +170,13 @@ impl Analyzer { for (path, positions) in &assignable_list { if positions.is_empty() { let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + let path: Vec<_> = path + .0 + .iter() + .map(|x| symbol_table::get(*x).unwrap().token.to_string()) + .collect(); ret.push(AnalyzerError::unassign_variable( - &symbol.token.to_string(), + &path.join("."), text, &symbol.token, )); @@ -187,7 +192,7 @@ impl Analyzer { } } - ret.append(&mut check_uncovered_branch(&symbol, text, positions)); + ret.append(&mut check_assign_position_tree(&symbol, text, positions)); } ret @@ -387,7 +392,7 @@ fn check_multiple_assignment( ret } -fn check_uncovered_branch( +fn check_assign_position_tree( symbol: &Symbol, text: &str, positions: &[(AssignPosition, bool)], @@ -410,5 +415,14 @@ fn check_uncovered_branch( )); } + if let Some(token) = tree.check_always_ff_missing_reset() { + ret.push(AnalyzerError::missing_reset_statement( + &symbol.token.to_string(), + text, + &symbol.token, + &token, + )); + } + ret } diff --git a/crates/analyzer/src/analyzer_error.rs b/crates/analyzer/src/analyzer_error.rs index b797fe50..5f0c0589 100644 --- a/crates/analyzer/src/analyzer_error.rs +++ b/crates/analyzer/src/analyzer_error.rs @@ -304,6 +304,8 @@ pub enum AnalyzerError { input: NamedSource, #[label("Error location")] error_location: SourceSpan, + #[label("Not reset")] + reset: SourceSpan, }, #[diagnostic( @@ -671,11 +673,12 @@ impl AnalyzerError { } } - pub fn missing_reset_statement(name: &str, source: &str, token: &Token) -> Self { + pub fn missing_reset_statement(name: &str, source: &str, token: &Token, reset: &Token) -> Self { AnalyzerError::MissingResetStatement { name: name.to_string(), input: AnalyzerError::named_source(source, token), error_location: token.into(), + reset: reset.into(), } } diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index 4644df95..132a5e02 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -1,8 +1,9 @@ +use crate::allow_table; use crate::analyzer_error::AnalyzerError; use crate::symbol::{Direction, SymbolId, SymbolKind}; use crate::symbol_table::{ - self, AssignDeclarationType, AssignPosition, AssignPositionType, AssignStatementBranchType, - ResolveSymbol, + self, AssignDeclarationType, AssignPosition, AssignPositionType, AssignStatementBranchItemType, + AssignStatementBranchType, ResolveSymbol, }; use std::collections::HashMap; use veryl_parser::veryl_grammar_trait::*; @@ -86,6 +87,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { AssignPositionType::StatementBranchItem { token: arg.else_token.token, index: self.branch_index, + r#type: AssignStatementBranchItemType::Else, } } else { AssignPositionType::DeclarationBranchItem { @@ -117,6 +119,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { self.assign_position.push(AssignPositionType::Statement { token: arg.equ.equ_token.token, + resettable: false, }); symbol_table::add_assign(x.full_path, &self.assign_position, false); self.assign_position.pop(); @@ -158,8 +161,10 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { } }; - self.assign_position - .push(AssignPositionType::Statement { token }); + self.assign_position.push(AssignPositionType::Statement { + token, + resettable: true, + }); symbol_table::add_assign(full_path, &self.assign_position, partial); self.assign_position.pop(); } else { @@ -193,12 +198,14 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { token: arg.r#if.if_token.token, branches, has_default, + allow_missing_reset_statement: false, r#type: AssignStatementBranchType::If, }); self.assign_position .push(AssignPositionType::StatementBranchItem { token: arg.r#if.if_token.token, index: self.branch_index, + r#type: AssignStatementBranchItemType::If, }); self.branch_index += 1; } @@ -218,17 +225,21 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { + arg.if_reset_statement_list0.len() + arg.if_reset_statement_opt.iter().len(); let has_default = arg.if_reset_statement_opt.is_some(); + let allow_missing_reset_statement = + allow_table::contains("missing_reset_statement"); self.assign_position .push(AssignPositionType::StatementBranch { token: arg.if_reset.if_reset_token.token, branches, has_default, + allow_missing_reset_statement, r#type: AssignStatementBranchType::IfReset, }); self.assign_position .push(AssignPositionType::StatementBranchItem { token: arg.if_reset.if_reset_token.token, index: self.branch_index, + r#type: AssignStatementBranchItemType::IfReset, }); self.branch_index += 1; } @@ -245,6 +256,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { self.assign_position.push(AssignPositionType::Statement { token: arg.r#for.for_token.token, + resettable: false, }); symbol_table::add_assign(x.full_path, &self.assign_position, false); self.assign_position.pop(); @@ -269,6 +281,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { token: arg.case.case_token.token, branches, has_default, + allow_missing_reset_statement: false, r#type: AssignStatementBranchType::Case, }); } @@ -286,6 +299,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { .push(AssignPositionType::StatementBranchItem { token: arg.colon.colon_token.token, index: self.branch_index, + r#type: AssignStatementBranchItemType::Case, }); self.branch_index += 1; } @@ -492,6 +506,7 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { if let Ok(x) = symbol_table::resolve(arg.identifier.as_ref()) { self.assign_position.push(AssignPositionType::Statement { token: arg.r#for.for_token.token, + resettable: false, }); symbol_table::add_assign(x.full_path, &self.assign_position, false); self.assign_position.pop(); diff --git a/crates/analyzer/src/handlers/check_reset.rs b/crates/analyzer/src/handlers/check_reset.rs index cc94125c..d3ab36c7 100644 --- a/crates/analyzer/src/handlers/check_reset.rs +++ b/crates/analyzer/src/handlers/check_reset.rs @@ -1,8 +1,7 @@ -use crate::allow_table; use crate::analyzer_error::AnalyzerError; use veryl_parser::veryl_grammar_trait::*; -use veryl_parser::veryl_walker::{Handler, HandlerPoint, VerylWalker}; -use veryl_parser::{ParolError, Stringifier}; +use veryl_parser::veryl_walker::{Handler, HandlerPoint}; +use veryl_parser::ParolError; #[derive(Default)] pub struct CheckReset<'a> { @@ -13,8 +12,6 @@ pub struct CheckReset<'a> { in_if_reset: bool, if_reset_brace: usize, if_reset_exist: bool, - all_lefthand_sides: Vec, - reset_lefthand_sides: Vec, } impl<'a> CheckReset<'a> { @@ -24,27 +21,6 @@ impl<'a> CheckReset<'a> { ..Default::default() } } - - fn get_identifier_path(x: &ExpressionIdentifier) -> Vec { - let mut ret = Vec::new(); - ret.push(x.identifier.identifier_token.to_string()); - match &*x.expression_identifier_group { - ExpressionIdentifierGroup::ExpressionIdentifierScoped(x) => { - let x = &x.expression_identifier_scoped; - ret.push(x.identifier.identifier_token.to_string()); - for x in &x.expression_identifier_scoped_list { - ret.push(x.identifier.identifier_token.to_string()); - } - } - ExpressionIdentifierGroup::ExpressionIdentifierMember(x) => { - let x = &x.expression_identifier_member; - for x in &x.expression_identifier_member_list0 { - ret.push(x.identifier.identifier_token.to_string()); - } - } - } - ret - } } impl<'a> Handler for CheckReset<'a> { @@ -75,22 +51,6 @@ impl<'a> VerylGrammarTrait for CheckReset<'a> { Ok(()) } - fn identifier_statement(&mut self, arg: &IdentifierStatement) -> Result<(), ParolError> { - if let HandlerPoint::Before = self.point { - if let IdentifierStatementGroup::Assignment(_) = &*arg.identifier_statement_group { - if self.in_always_ff { - self.all_lefthand_sides - .push(*arg.expression_identifier.clone()); - if self.in_if_reset { - self.reset_lefthand_sides - .push(*arg.expression_identifier.clone()); - } - } - } - } - Ok(()) - } - fn if_reset(&mut self, _arg: &IfReset) -> Result<(), ParolError> { if let HandlerPoint::Before = self.point { self.if_reset_exist = true; @@ -130,32 +90,6 @@ impl<'a> VerylGrammarTrait for CheckReset<'a> { )); } - // Check lefthand side values which is not reset - let mut reset_lefthand_sides = Vec::new(); - for x in &self.reset_lefthand_sides { - reset_lefthand_sides.push(Self::get_identifier_path(x)); - } - - for x in &self.all_lefthand_sides { - let mut stringifier = Stringifier::new(); - stringifier.expression_identifier(x); - let name = stringifier.as_str().to_string(); - let path = Self::get_identifier_path(x); - - if self.if_reset_exist - && !allow_table::contains("missing_reset_statement") - && !reset_lefthand_sides.iter().any(|x| path.starts_with(x)) - { - self.errors.push(AnalyzerError::missing_reset_statement( - &name, - self.text, - &x.identifier.identifier_token.token, - )); - } - } - - self.all_lefthand_sides.clear(); - self.reset_lefthand_sides.clear(); self.in_always_ff = false; self.if_reset_exist = false; } diff --git a/crates/analyzer/src/symbol_table.rs b/crates/analyzer/src/symbol_table.rs index f22ea72f..704c6e2a 100644 --- a/crates/analyzer/src/symbol_table.rs +++ b/crates/analyzer/src/symbol_table.rs @@ -330,14 +330,17 @@ pub enum AssignPositionType { token: Token, branches: usize, has_default: bool, + allow_missing_reset_statement: bool, r#type: AssignStatementBranchType, }, StatementBranchItem { token: Token, index: usize, + r#type: AssignStatementBranchItemType, }, Statement { token: Token, + resettable: bool, }, } @@ -371,6 +374,14 @@ pub enum AssignStatementBranchType { Case, } +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignStatementBranchItemType { + If, + IfReset, + Else, + Case, +} + #[derive(Clone, Default, Debug)] pub struct AssignPositionTree { r#type: Option, @@ -450,6 +461,46 @@ impl AssignPositionTree { _ => unreachable!(), } } + + pub fn check_always_ff_missing_reset(&self) -> Option { + if let Some(AssignPositionType::StatementBranch { + ref r#type, + ref token, + ref allow_missing_reset_statement, + .. + }) = self.r#type + { + if *r#type == AssignStatementBranchType::IfReset + && !allow_missing_reset_statement + && self.is_resettable() + { + if let Some(AssignPositionType::StatementBranchItem { ref r#type, .. }) = + self.children[0].r#type + { + if *r#type != AssignStatementBranchItemType::IfReset { + return Some(*token); + } + } + } + } + + for child in &self.children { + let ret = child.check_always_ff_missing_reset(); + if ret.is_some() { + return ret; + } + } + + None + } + + fn is_resettable(&self) -> bool { + if let Some(AssignPositionType::Statement { resettable, .. }) = self.r#type { + resettable + } else { + self.children.iter().any(|x| x.is_resettable()) + } + } } #[derive(Clone, Default, Debug)] From 0c115587f9f76429a88edb69107b0e99ae8814e7 Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 17:22:32 +0900 Subject: [PATCH 6/9] Refactor analyzer pass3 --- crates/analyzer/src/analyzer.rs | 185 +++++++----- crates/analyzer/src/assign.rs | 277 ++++++++++++++++++ .../analyzer/src/handlers/check_assignment.rs | 9 +- crates/analyzer/src/lib.rs | 1 + crates/analyzer/src/symbol_table.rs | 274 +---------------- 5 files changed, 389 insertions(+), 357 deletions(-) create mode 100644 crates/analyzer/src/assign.rs diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index 36ede07e..da67add4 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -1,12 +1,12 @@ +use crate::analyzer::resource_table::PathId; use crate::analyzer_error::AnalyzerError; +use crate::assign::{AssignPath, AssignPosition, AssignPositionTree, AssignPositionType}; use crate::handlers::*; use crate::namespace_table; use crate::symbol::{ Direction, ParameterValue, Symbol, SymbolId, SymbolKind, TypeKind, VariableAffiniation, }; -use crate::symbol_table::{ - self, AssignPath, AssignPosition, AssignPositionTree, AssignPositionType, ResolveSymbol, -}; +use crate::symbol_table::{self, ResolveSymbol}; use itertools::Itertools; use std::path::Path; use veryl_metadata::{Lint, Metadata}; @@ -50,6 +50,105 @@ impl<'a> VerylWalker for AnalyzerPass2<'a> { } } +pub struct AnalyzerPass3<'a> { + path: PathId, + text: &'a str, + symbols: Vec, +} + +impl<'a> AnalyzerPass3<'a> { + pub fn new(path: &'a Path, text: &'a str) -> Self { + let symbols = symbol_table::get_all(); + let path = resource_table::get_path_id(path.to_path_buf()).unwrap(); + AnalyzerPass3 { + path, + text, + symbols, + } + } + + pub fn check_unused_variables(&self) -> Vec { + let mut ret = Vec::new(); + + for symbol in &self.symbols { + if symbol.token.source == self.path { + if let SymbolKind::Variable(_) = symbol.kind { + if symbol.references.is_empty() && !symbol.allow_unused { + let name = symbol.token.to_string(); + if name.starts_with('_') { + continue; + } + + ret.push(AnalyzerError::unused_variable( + &symbol.token.to_string(), + self.text, + &symbol.token, + )); + } + } + } + } + + ret + } + + pub fn check_assignment(&self) -> Vec { + let mut ret = Vec::new(); + + let assign_list = symbol_table::get_assign_list(); + let mut assignable_list = Vec::new(); + + for symbol in &self.symbols { + if symbol.token.source == self.path { + assignable_list.append(&mut traverse_assignable_symbol( + symbol.id, + &AssignPath::new(symbol.id), + )); + } + } + let mut assignable_list: Vec<_> = assignable_list.iter().map(|x| (x, vec![])).collect(); + for assign in &assign_list { + for assignable in &mut assignable_list { + if assignable.0.included(&assign.path) { + assignable.1.push((assign.position.clone(), assign.partial)); + } + } + } + + for (path, positions) in &assignable_list { + if positions.is_empty() { + let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + let path: Vec<_> = path + .0 + .iter() + .map(|x| symbol_table::get(*x).unwrap().token.to_string()) + .collect(); + ret.push(AnalyzerError::unassign_variable( + &path.join("."), + self.text, + &symbol.token, + )); + } + + let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); + + if positions.len() > 1 { + for comb in positions.iter().combinations(2) { + ret.append(&mut check_multiple_assignment( + &symbol, self.text, comb[0], comb[1], + )); + } + } + + ret.append(&mut check_assign_position_tree( + &symbol, self.text, positions, + )); + } + + ret + } +} + pub struct Analyzer { lint_opt: Lint, } @@ -117,83 +216,9 @@ impl Analyzer { let mut ret = Vec::new(); namespace_table::set_default(&[project_name.into()]); - ret.append(&mut Analyzer::check_symbol_table(path.as_ref(), text)); - - ret - } - - fn check_symbol_table(path: &Path, text: &str) -> Vec { - let mut ret = Vec::new(); - let symbols = symbol_table::get_all(); - - // check unused variables - let path = resource_table::get_path_id(path.to_path_buf()).unwrap(); - for symbol in &symbols { - if symbol.token.source == path { - if let SymbolKind::Variable(_) = symbol.kind { - if symbol.references.is_empty() && !symbol.allow_unused { - let name = symbol.token.to_string(); - if name.starts_with('_') { - continue; - } - - ret.push(AnalyzerError::unused_variable( - &symbol.token.to_string(), - text, - &symbol.token, - )); - } - } - } - } - - // check assignment - let assign_list = symbol_table::get_assign_list(); - let mut assignable_list = Vec::new(); - for symbol in &symbols { - if symbol.token.source == path { - assignable_list.append(&mut traverse_assignable_symbol( - symbol.id, - &AssignPath::new(symbol.id), - )); - } - } - let mut assignable_list: Vec<_> = assignable_list.iter().map(|x| (x, vec![])).collect(); - for assign in &assign_list { - for assignable in &mut assignable_list { - if assignable.0.included(&assign.path) { - assignable.1.push((assign.position.clone(), assign.partial)); - } - } - } - - for (path, positions) in &assignable_list { - if positions.is_empty() { - let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); - let path: Vec<_> = path - .0 - .iter() - .map(|x| symbol_table::get(*x).unwrap().token.to_string()) - .collect(); - ret.push(AnalyzerError::unassign_variable( - &path.join("."), - text, - &symbol.token, - )); - } - - let symbol = symbol_table::get(*path.0.first().unwrap()).unwrap(); - - if positions.len() > 1 { - for comb in positions.iter().combinations(2) { - ret.append(&mut check_multiple_assignment( - &symbol, text, comb[0], comb[1], - )); - } - } - - ret.append(&mut check_assign_position_tree(&symbol, text, positions)); - } + let pass3 = AnalyzerPass3::new(path.as_ref(), text); + ret.append(&mut pass3.check_unused_variables()); + ret.append(&mut pass3.check_assignment()); ret } diff --git a/crates/analyzer/src/assign.rs b/crates/analyzer/src/assign.rs new file mode 100644 index 00000000..bcdeee60 --- /dev/null +++ b/crates/analyzer/src/assign.rs @@ -0,0 +1,277 @@ +use crate::symbol::SymbolId; +use crate::symbol_table; +use std::fmt; +use veryl_parser::veryl_token::Token; + +#[derive(Clone, Debug)] +pub struct Assign { + pub path: AssignPath, + pub position: AssignPosition, + pub partial: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct AssignPath(pub Vec); + +impl AssignPath { + pub fn new(x: SymbolId) -> Self { + Self(vec![x]) + } + + pub fn push(&mut self, x: SymbolId) { + self.0.push(x) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } + + pub fn included(&self, x: &AssignPath) -> bool { + for (i, x) in x.0.iter().enumerate() { + if let Some(path) = self.0.get(i) { + if path != x { + return false; + } + } else { + return false; + } + } + true + } +} + +impl fmt::Display for AssignPath { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ret = "".to_string(); + for (i, id) in self.0.iter().enumerate() { + if let Some(symbol) = symbol_table::get(*id) { + if i != 0 { + ret.push('.'); + } + ret.push_str(&symbol.token.to_string()); + } + } + ret.fmt(f) + } +} + +#[derive(Clone, Default, Debug)] +pub struct AssignPosition(pub Vec); + +impl AssignPosition { + pub fn new(x: AssignPositionType) -> Self { + Self(vec![x]) + } + + pub fn push(&mut self, x: AssignPositionType) { + self.0.push(x) + } + + pub fn pop(&mut self) -> Option { + self.0.pop() + } +} + +impl fmt::Display for AssignPosition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut ret = "".to_string(); + for (i, x) in self.0.iter().enumerate() { + if i != 0 { + ret.push('.'); + } + ret.push_str(&x.token().to_string()); + } + ret.fmt(f) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignPositionType { + DeclarationBranch { + token: Token, + branches: usize, + }, + DeclarationBranchItem { + token: Token, + index: usize, + }, + Declaration { + token: Token, + r#type: AssignDeclarationType, + }, + StatementBranch { + token: Token, + branches: usize, + has_default: bool, + allow_missing_reset_statement: bool, + r#type: AssignStatementBranchType, + }, + StatementBranchItem { + token: Token, + index: usize, + r#type: AssignStatementBranchItemType, + }, + Statement { + token: Token, + resettable: bool, + }, +} + +impl AssignPositionType { + pub fn token(&self) -> &Token { + match self { + AssignPositionType::DeclarationBranch { token: x, .. } => x, + AssignPositionType::DeclarationBranchItem { token: x, .. } => x, + AssignPositionType::Declaration { token: x, .. } => x, + AssignPositionType::StatementBranch { token: x, .. } => x, + AssignPositionType::StatementBranchItem { token: x, .. } => x, + AssignPositionType::Statement { token: x, .. } => x, + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignDeclarationType { + Let, + AlwaysFf, + AlwaysComb, + Assign, + Inst, + Function, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignStatementBranchType { + If, + IfReset, + Case, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AssignStatementBranchItemType { + If, + IfReset, + Else, + Case, +} + +#[derive(Clone, Default, Debug)] +pub struct AssignPositionTree { + r#type: Option, + children: Vec, +} + +impl AssignPositionTree { + pub fn add(&mut self, mut pos: AssignPosition) { + if pos.0.is_empty() { + return; + } + + let mut head: Vec<_> = pos.0.drain(0..1).collect(); + + for child in &mut self.children { + if child.r#type.as_ref() == head.first() { + child.add(pos); + return; + } + } + + let mut node = AssignPositionTree { + r#type: Some(head.remove(0)), + children: vec![], + }; + node.add(pos); + self.children.push(node); + } + + pub fn check_always_comb_uncovered(&self) -> Option { + if let Some(AssignPositionType::Declaration { ref r#type, .. }) = self.r#type { + if *r#type == AssignDeclarationType::AlwaysComb { + return self + .children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten(); + } + } + + for child in &self.children { + let ret = child.check_always_comb_uncovered(); + if ret.is_some() { + return ret; + } + } + + None + } + + fn impl_always_comb_uncovered(&self) -> Option { + match self.r#type { + Some(AssignPositionType::StatementBranch { + token, + branches, + has_default, + .. + }) => { + if !has_default || self.children.len() != branches { + Some(token) + } else { + self.children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten() + } + } + Some(AssignPositionType::StatementBranchItem { .. }) => self + .children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .find(|x| x.is_some()) + .flatten(), + Some(AssignPositionType::Statement { .. }) => None, + _ => unreachable!(), + } + } + + pub fn check_always_ff_missing_reset(&self) -> Option { + if let Some(AssignPositionType::StatementBranch { + ref r#type, + ref token, + ref allow_missing_reset_statement, + .. + }) = self.r#type + { + if *r#type == AssignStatementBranchType::IfReset + && !allow_missing_reset_statement + && self.is_resettable() + { + if let Some(AssignPositionType::StatementBranchItem { ref r#type, .. }) = + self.children[0].r#type + { + if *r#type != AssignStatementBranchItemType::IfReset { + return Some(*token); + } + } + } + } + + for child in &self.children { + let ret = child.check_always_ff_missing_reset(); + if ret.is_some() { + return ret; + } + } + + None + } + + fn is_resettable(&self) -> bool { + if let Some(AssignPositionType::Statement { resettable, .. }) = self.r#type { + resettable + } else { + self.children.iter().any(|x| x.is_resettable()) + } + } +} diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index 132a5e02..c5d37123 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -1,10 +1,11 @@ use crate::allow_table; use crate::analyzer_error::AnalyzerError; -use crate::symbol::{Direction, SymbolId, SymbolKind}; -use crate::symbol_table::{ - self, AssignDeclarationType, AssignPosition, AssignPositionType, AssignStatementBranchItemType, - AssignStatementBranchType, ResolveSymbol, +use crate::assign::{ + AssignDeclarationType, AssignPosition, AssignPositionType, AssignStatementBranchItemType, + AssignStatementBranchType, }; +use crate::symbol::{Direction, SymbolId, SymbolKind}; +use crate::symbol_table::{self, ResolveSymbol}; use std::collections::HashMap; use veryl_parser::veryl_grammar_trait::*; use veryl_parser::veryl_walker::{Handler, HandlerPoint}; diff --git a/crates/analyzer/src/lib.rs b/crates/analyzer/src/lib.rs index 74950a48..4b044b7a 100644 --- a/crates/analyzer/src/lib.rs +++ b/crates/analyzer/src/lib.rs @@ -1,6 +1,7 @@ pub mod allow_table; pub mod analyzer; pub mod analyzer_error; +pub mod assign; pub mod evaluator; pub mod handlers; pub mod msb_table; diff --git a/crates/analyzer/src/symbol_table.rs b/crates/analyzer/src/symbol_table.rs index 704c6e2a..85245a0c 100644 --- a/crates/analyzer/src/symbol_table.rs +++ b/crates/analyzer/src/symbol_table.rs @@ -1,3 +1,4 @@ +use crate::assign::{Assign, AssignPath, AssignPosition}; use crate::evaluator::Evaluated; use crate::namespace::Namespace; use crate::namespace_table; @@ -230,279 +231,6 @@ impl ResolveError { } } -#[derive(Clone, Debug)] -pub struct Assign { - pub path: AssignPath, - pub position: AssignPosition, - pub partial: bool, -} - -#[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct AssignPath(pub Vec); - -impl AssignPath { - pub fn new(x: SymbolId) -> Self { - Self(vec![x]) - } - - pub fn push(&mut self, x: SymbolId) { - self.0.push(x) - } - - pub fn pop(&mut self) -> Option { - self.0.pop() - } - - pub fn included(&self, x: &AssignPath) -> bool { - for (i, x) in x.0.iter().enumerate() { - if let Some(path) = self.0.get(i) { - if path != x { - return false; - } - } else { - return false; - } - } - true - } -} - -impl fmt::Display for AssignPath { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut ret = "".to_string(); - for (i, id) in self.0.iter().enumerate() { - if let Some(symbol) = get(*id) { - if i != 0 { - ret.push('.'); - } - ret.push_str(&symbol.token.to_string()); - } - } - ret.fmt(f) - } -} - -#[derive(Clone, Default, Debug)] -pub struct AssignPosition(pub Vec); - -impl AssignPosition { - pub fn new(x: AssignPositionType) -> Self { - Self(vec![x]) - } - - pub fn push(&mut self, x: AssignPositionType) { - self.0.push(x) - } - - pub fn pop(&mut self) -> Option { - self.0.pop() - } -} - -impl fmt::Display for AssignPosition { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let mut ret = "".to_string(); - for (i, x) in self.0.iter().enumerate() { - if i != 0 { - ret.push('.'); - } - ret.push_str(&x.token().to_string()); - } - ret.fmt(f) - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum AssignPositionType { - DeclarationBranch { - token: Token, - branches: usize, - }, - DeclarationBranchItem { - token: Token, - index: usize, - }, - Declaration { - token: Token, - r#type: AssignDeclarationType, - }, - StatementBranch { - token: Token, - branches: usize, - has_default: bool, - allow_missing_reset_statement: bool, - r#type: AssignStatementBranchType, - }, - StatementBranchItem { - token: Token, - index: usize, - r#type: AssignStatementBranchItemType, - }, - Statement { - token: Token, - resettable: bool, - }, -} - -impl AssignPositionType { - pub fn token(&self) -> &Token { - match self { - AssignPositionType::DeclarationBranch { token: x, .. } => x, - AssignPositionType::DeclarationBranchItem { token: x, .. } => x, - AssignPositionType::Declaration { token: x, .. } => x, - AssignPositionType::StatementBranch { token: x, .. } => x, - AssignPositionType::StatementBranchItem { token: x, .. } => x, - AssignPositionType::Statement { token: x, .. } => x, - } - } -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum AssignDeclarationType { - Let, - AlwaysFf, - AlwaysComb, - Assign, - Inst, - Function, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum AssignStatementBranchType { - If, - IfReset, - Case, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum AssignStatementBranchItemType { - If, - IfReset, - Else, - Case, -} - -#[derive(Clone, Default, Debug)] -pub struct AssignPositionTree { - r#type: Option, - children: Vec, -} - -impl AssignPositionTree { - pub fn add(&mut self, mut pos: AssignPosition) { - if pos.0.is_empty() { - return; - } - - let mut head: Vec<_> = pos.0.drain(0..1).collect(); - - for child in &mut self.children { - if child.r#type.as_ref() == head.first() { - child.add(pos); - return; - } - } - - let mut node = AssignPositionTree { - r#type: Some(head.remove(0)), - children: vec![], - }; - node.add(pos); - self.children.push(node); - } - - pub fn check_always_comb_uncovered(&self) -> Option { - if let Some(AssignPositionType::Declaration { ref r#type, .. }) = self.r#type { - if *r#type == AssignDeclarationType::AlwaysComb { - return self - .children - .iter() - .map(|x| x.impl_always_comb_uncovered()) - .find(|x| x.is_some()) - .flatten(); - } - } - - for child in &self.children { - let ret = child.check_always_comb_uncovered(); - if ret.is_some() { - return ret; - } - } - - None - } - - fn impl_always_comb_uncovered(&self) -> Option { - match self.r#type { - Some(AssignPositionType::StatementBranch { - token, - branches, - has_default, - .. - }) => { - if !has_default || self.children.len() != branches { - Some(token) - } else { - self.children - .iter() - .map(|x| x.impl_always_comb_uncovered()) - .find(|x| x.is_some()) - .flatten() - } - } - Some(AssignPositionType::StatementBranchItem { .. }) => self - .children - .iter() - .map(|x| x.impl_always_comb_uncovered()) - .find(|x| x.is_some()) - .flatten(), - Some(AssignPositionType::Statement { .. }) => None, - _ => unreachable!(), - } - } - - pub fn check_always_ff_missing_reset(&self) -> Option { - if let Some(AssignPositionType::StatementBranch { - ref r#type, - ref token, - ref allow_missing_reset_statement, - .. - }) = self.r#type - { - if *r#type == AssignStatementBranchType::IfReset - && !allow_missing_reset_statement - && self.is_resettable() - { - if let Some(AssignPositionType::StatementBranchItem { ref r#type, .. }) = - self.children[0].r#type - { - if *r#type != AssignStatementBranchItemType::IfReset { - return Some(*token); - } - } - } - } - - for child in &self.children { - let ret = child.check_always_ff_missing_reset(); - if ret.is_some() { - return ret; - } - } - - None - } - - fn is_resettable(&self) -> bool { - if let Some(AssignPositionType::Statement { resettable, .. }) = self.r#type { - resettable - } else { - self.children.iter().any(|x| x.is_resettable()) - } - } -} - #[derive(Clone, Default, Debug)] pub struct SymbolTable { name_table: HashMap>, From 422d252558ae50b65983beb701cf5db2998e37b9 Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 17:42:45 +0900 Subject: [PATCH 7/9] Fix unexpected assignment error --- crates/analyzer/src/assign.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/analyzer/src/assign.rs b/crates/analyzer/src/assign.rs index bcdeee60..0120bfb2 100644 --- a/crates/analyzer/src/assign.rs +++ b/crates/analyzer/src/assign.rs @@ -187,12 +187,16 @@ impl AssignPositionTree { pub fn check_always_comb_uncovered(&self) -> Option { if let Some(AssignPositionType::Declaration { ref r#type, .. }) = self.r#type { if *r#type == AssignDeclarationType::AlwaysComb { - return self + let children: Vec<_> = self .children .iter() .map(|x| x.impl_always_comb_uncovered()) - .find(|x| x.is_some()) - .flatten(); + .collect(); + if children.iter().any(|x| x.is_none()) { + return None; + } else { + return children.into_iter().find(|x| x.is_some()).flatten(); + } } } @@ -224,12 +228,18 @@ impl AssignPositionTree { .flatten() } } - Some(AssignPositionType::StatementBranchItem { .. }) => self - .children - .iter() - .map(|x| x.impl_always_comb_uncovered()) - .find(|x| x.is_some()) - .flatten(), + Some(AssignPositionType::StatementBranchItem { .. }) => { + let children: Vec<_> = self + .children + .iter() + .map(|x| x.impl_always_comb_uncovered()) + .collect(); + if children.iter().any(|x| x.is_none()) { + None + } else { + children.into_iter().find(|x| x.is_some()).flatten() + } + } Some(AssignPositionType::Statement { .. }) => None, _ => unreachable!(), } From 3dccac8d17a219b6c4eceef84b80cb5d95138bef Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 17:45:45 +0900 Subject: [PATCH 8/9] Suppress unexpected error of veryl-ls --- crates/languageserver/src/server.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/languageserver/src/server.rs b/crates/languageserver/src/server.rs index 1dee995d..f526c134 100644 --- a/crates/languageserver/src/server.rs +++ b/crates/languageserver/src/server.rs @@ -626,6 +626,7 @@ impl Server { !matches!( x, AnalyzerError::UndefinedIdentifier { .. } + | AnalyzerError::UnknownMember { .. } | AnalyzerError::UnassignVariable { .. } ) } From bfb9b3bc5269c8a493d1325b6481aff862b1eb10 Mon Sep 17 00:00:00 2001 From: dalance Date: Thu, 28 Mar 2024 18:29:24 +0900 Subject: [PATCH 9/9] Fix unexpected error --- crates/analyzer/src/analyzer.rs | 6 +++++ crates/analyzer/src/assign.rs | 24 ++++++++++++++----- .../analyzer/src/handlers/check_assignment.rs | 21 +++++++++------- .../src/handlers/create_symbol_table.rs | 6 ++--- crates/analyzer/src/symbol.rs | 2 +- crates/parser/src/veryl_token.rs | 4 ++-- 6 files changed, 43 insertions(+), 20 deletions(-) diff --git a/crates/analyzer/src/analyzer.rs b/crates/analyzer/src/analyzer.rs index da67add4..d6033c1d 100644 --- a/crates/analyzer/src/analyzer.rs +++ b/crates/analyzer/src/analyzer.rs @@ -392,6 +392,12 @@ fn check_multiple_assignment( let mut ret = Vec::new(); let len = x_pos.0.len().min(y_pos.0.len()); + let x_maybe = x_pos.0.last().unwrap().is_maybe(); + let y_maybe = y_pos.0.last().unwrap().is_maybe(); + if x_maybe || y_maybe { + return vec![]; + } + for i in 0..len { let x_type = &x_pos.0[i]; let y_type = &y_pos.0[i]; diff --git a/crates/analyzer/src/assign.rs b/crates/analyzer/src/assign.rs index 0120bfb2..e2e26ff7 100644 --- a/crates/analyzer/src/assign.rs +++ b/crates/analyzer/src/assign.rs @@ -115,17 +115,29 @@ pub enum AssignPositionType { token: Token, resettable: bool, }, + Connect { + token: Token, + maybe: bool, + }, } impl AssignPositionType { pub fn token(&self) -> &Token { match self { - AssignPositionType::DeclarationBranch { token: x, .. } => x, - AssignPositionType::DeclarationBranchItem { token: x, .. } => x, - AssignPositionType::Declaration { token: x, .. } => x, - AssignPositionType::StatementBranch { token: x, .. } => x, - AssignPositionType::StatementBranchItem { token: x, .. } => x, - AssignPositionType::Statement { token: x, .. } => x, + AssignPositionType::DeclarationBranch { token, .. } => token, + AssignPositionType::DeclarationBranchItem { token, .. } => token, + AssignPositionType::Declaration { token, .. } => token, + AssignPositionType::StatementBranch { token, .. } => token, + AssignPositionType::StatementBranchItem { token, .. } => token, + AssignPositionType::Statement { token, .. } => token, + AssignPositionType::Connect { token, .. } => token, + } + } + + pub fn is_maybe(&self) -> bool { + match self { + AssignPositionType::Connect { maybe, .. } => *maybe, + _ => false, } } } diff --git a/crates/analyzer/src/handlers/check_assignment.rs b/crates/analyzer/src/handlers/check_assignment.rs index c5d37123..24ee2f8b 100644 --- a/crates/analyzer/src/handlers/check_assignment.rs +++ b/crates/analyzer/src/handlers/check_assignment.rs @@ -423,9 +423,14 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { } } - for (name, target) in &x.connects { + self.assign_position.push(AssignPositionType::Declaration { + token: arg.inst.inst_token.token, + r#type: AssignDeclarationType::Inst, + }); + + for (token, target) in &x.connects { if !target.is_empty() { - let dir_output = if let Some(dir) = dirs.get(name) { + let dir_output = if let Some(dir) = dirs.get(&token.text) { matches!( dir, Direction::Ref | Direction::Inout | Direction::Output @@ -438,12 +443,10 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { if let Ok(x) = symbol_table::resolve((target, &symbol.namespace)) { - self.assign_position.push( - AssignPositionType::Declaration { - token: arg.inst.inst_token.token, - r#type: AssignDeclarationType::Inst, - }, - ); + self.assign_position.push(AssignPositionType::Connect { + token: *token, + maybe: dir_unknown, + }); symbol_table::add_assign( x.full_path, &self.assign_position, @@ -454,6 +457,8 @@ impl<'a> VerylGrammarTrait for CheckAssignment<'a> { } } } + + self.assign_position.pop(); } } } diff --git a/crates/analyzer/src/handlers/create_symbol_table.rs b/crates/analyzer/src/handlers/create_symbol_table.rs index 91558d03..0203bff8 100644 --- a/crates/analyzer/src/handlers/create_symbol_table.rs +++ b/crates/analyzer/src/handlers/create_symbol_table.rs @@ -35,7 +35,7 @@ pub struct CreateSymbolTable<'a> { struct_union_members: Vec>, affiniation: Vec, connect_targets: Vec>, - connects: HashMap>, + connects: HashMap>, } #[derive(Clone)] @@ -377,11 +377,11 @@ impl<'a> VerylGrammarTrait for CreateSymbolTable<'a> { match self.point { HandlerPoint::Before => self.connect_targets.clear(), HandlerPoint::After => { - let port = arg.identifier.identifier_token.token.text; + let port = arg.identifier.identifier_token.token; let targets = if arg.inst_port_item_opt.is_some() { self.connect_targets.drain(0..).collect() } else { - vec![vec![port]] + vec![vec![port.text]] }; for target in targets { self.connects.insert(port, target); diff --git a/crates/analyzer/src/symbol.rs b/crates/analyzer/src/symbol.rs index 0a849e22..797b4e63 100644 --- a/crates/analyzer/src/symbol.rs +++ b/crates/analyzer/src/symbol.rs @@ -623,7 +623,7 @@ pub struct FunctionProperty { #[derive(Debug, Clone)] pub struct InstanceProperty { pub type_name: Vec, - pub connects: HashMap>, + pub connects: HashMap>, } #[derive(Debug, Clone)] diff --git a/crates/parser/src/veryl_token.rs b/crates/parser/src/veryl_token.rs index fde2e14c..f4e8b904 100644 --- a/crates/parser/src/veryl_token.rs +++ b/crates/parser/src/veryl_token.rs @@ -6,7 +6,7 @@ use paste::paste; use regex::Regex; use std::fmt; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum TokenSource { File(PathId), Builtin, @@ -33,7 +33,7 @@ impl PartialEq for TokenSource { } } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct Token { pub id: TokenId, pub text: StrId,