Skip to content

Commit

Permalink
Add uncovered_branch error
Browse files Browse the repository at this point in the history
  • Loading branch information
dalance committed Mar 28, 2024
1 parent 068c4b9 commit 6c9dc7a
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 4 deletions.
42 changes: 38 additions & 4 deletions crates/analyzer/src/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -341,7 +349,7 @@ fn traverse_assignable_symbol(id: SymbolId, path: &AssignPath) -> Vec<AssignPath
vec![]
}

fn check_assign_position(
fn check_multiple_assignment(
symbol: &Symbol,
text: &str,
x: &(AssignPosition, bool),
Expand Down Expand Up @@ -378,3 +386,29 @@ fn check_assign_position(

ret
}

fn check_uncovered_branch(
symbol: &Symbol,
text: &str,
positions: &[(AssignPosition, bool)],
) -> Vec<AnalyzerError> {
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
}
31 changes: 31 additions & 0 deletions crates/analyzer/src/analyzer_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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(),
}
}
}
11 changes: 11 additions & 0 deletions crates/analyzer/src/handlers/check_assignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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,
});
}
Expand Down
82 changes: 82 additions & 0 deletions crates/analyzer/src/symbol_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ pub enum AssignPositionType {
StatementBranch {
token: Token,
branches: usize,
has_default: bool,
r#type: AssignStatementBranchType,
},
StatementBranchItem {
Expand Down Expand Up @@ -370,6 +371,87 @@ pub enum AssignStatementBranchType {
Case,
}

#[derive(Clone, Default, Debug)]
pub struct AssignPositionTree {
r#type: Option<AssignPositionType>,
children: Vec<AssignPositionTree>,
}

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<Token> {
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<Token> {
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<StrId, Vec<SymbolId>>,
Expand Down
19 changes: 19 additions & 0 deletions crates/analyzer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 { .. }));
}

0 comments on commit 6c9dc7a

Please sign in to comment.