Skip to content

Commit

Permalink
Merge pull request #783 from taichi-ishitani/enhance_case_statement_e…
Browse files Browse the repository at this point in the history
…xpression

Enhance case statement/expression
  • Loading branch information
dalance authored Jun 14, 2024
2 parents 469b016 + 5769193 commit 7c483e6
Show file tree
Hide file tree
Showing 30 changed files with 19,246 additions and 17,250 deletions.
21 changes: 21 additions & 0 deletions crates/analyzer/src/analyzer_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,20 @@ pub enum AnalyzerError {
error_location: SourceSpan,
},

#[diagnostic(
severity(Error),
code(invalid_case_condition_non_elaborative),
help(""),
url("")
)]
#[error("Case condition value cannot be used because it is not evaluable at elaboration time")]
InvalidCaseConditionNonElaborative {
#[source_code]
input: NamedSource,
#[label("Error location")]
error_location: SourceSpan,
},

#[diagnostic(
severity(Error),
code(missing_default_argument),
Expand Down Expand Up @@ -1010,6 +1024,13 @@ impl AnalyzerError {
}
}

pub fn invalid_case_condition_non_elaborative(source: &str, token: &TokenRange) -> Self {
AnalyzerError::InvalidCaseConditionNonElaborative {
input: AnalyzerError::named_source(source, token),
error_location: token.into(),
}
}

pub fn invalid_modport_variable_item(
identifier: &str,
source: &str,
Expand Down
5 changes: 5 additions & 0 deletions crates/analyzer/src/evaluator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,7 @@ impl Evaluator {
}
Factor::IfExpression(x) => self.if_expression(&x.if_expression),
Factor::CaseExpression(x) => self.case_expression(&x.case_expression),
Factor::SwitchExpression(x) => self.switch_expression(&x.switch_expression),
Factor::StringLiteral(_) => Evaluated::Unknown,
Factor::FactorGroup(_) => Evaluated::Unknown,
Factor::InsideExpression(_) => Evaluated::Unknown,
Expand Down Expand Up @@ -787,4 +788,8 @@ impl Evaluator {
fn case_expression(&mut self, _arg: &CaseExpression) -> Evaluated {
Evaluated::Unknown
}

fn switch_expression(&mut self, _arg: &SwitchExpression) -> Evaluated {
Evaluated::Unknown
}
}
27 changes: 27 additions & 0 deletions crates/analyzer/src/handlers/check_expression.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::analyzer_error::AnalyzerError;
use crate::evaluator::{Evaluated, Evaluator};
use crate::symbol::SymbolKind;
use crate::symbol_table;
use veryl_parser::veryl_grammar_trait::*;
Expand All @@ -11,6 +12,8 @@ pub struct CheckExpression<'a> {
pub errors: Vec<AnalyzerError>,
text: &'a str,
point: HandlerPoint,
case_condition_depth: usize,
evaluator: Evaluator,
}

impl<'a> CheckExpression<'a> {
Expand All @@ -29,6 +32,30 @@ impl<'a> Handler for CheckExpression<'a> {
}

impl<'a> VerylGrammarTrait for CheckExpression<'a> {
fn case_condition(&mut self, _arg: &CaseCondition) -> Result<(), ParolError> {
match self.point {
HandlerPoint::Before => self.case_condition_depth += 1,
HandlerPoint::After => self.case_condition_depth -= 1,
}
Ok(())
}

fn expression(&mut self, arg: &Expression) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
if self.case_condition_depth >= 1 {
let result = matches!(self.evaluator.expression(arg), Evaluated::Variable { .. });
if result {
self.errors
.push(AnalyzerError::invalid_case_condition_non_elaborative(
self.text,
&arg.into(),
));
}
}
}
Ok(())
}

fn factor(&mut self, arg: &Factor) -> Result<(), ParolError> {
if let HandlerPoint::Before = self.point {
if let Factor::ExpressionIdentifierFactorOpt(x) = arg {
Expand Down
131 changes: 127 additions & 4 deletions crates/analyzer/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1643,24 +1643,147 @@ fn enum_non_const_exception() {
i_clk: input clock,
i_rst: input reset,
) {
enum State: logic<3> {
Idle = 3'bxx1,
Run0 = 3'b000,
Run1 = 3'b010,
Run2 = 3'b100,
Done = 3'b110,
}
var state: State;
always_ff {
if_reset {
state = State::Idle;
}
}
}"#;
let errors = analyze(code);
assert!(errors.is_empty());
}

#[test]
fn invalid_case_condition_expression() {
let code = r#"
module ModuleA (
i_sel: input logic<3>,
i_a : input logic<4>,
o_b : output logic,
o_c : output logic,
) {
local ONE: bit <3> = 3'd1;
always_comb {
case i_sel {
3'd0 : o_b = i_a[0];
ONE : o_b = i_a[1];
2..=3 : o_b = i_a[2];
3'b1xx : o_b = i_a[3];
default: o_b = i_a[3];
}
}
assign o_c = case i_sel {
3'd0 : i_a[0],
ONE : i_a[1],
2..=3 : i_a[2],
3'b1xx : i_a[3],
default: i_a[3],
};
}
"#;

let errors = analyze(code);
assert!(errors.is_empty());

let code = r#"
module ModuleB (
i_sel: input logic<2>,
i_a : input logic<3>,
o_b : output logic,
) {
let c: logic<2> = 2'd0;
always_comb {
case i_sel {
c : o_b = i_a[0];
default: o_b = i_a[1];
}
}
}
"#;

let errors = analyze(code);
assert!(matches!(
errors[0],
AnalyzerError::InvalidCaseConditionNonElaborative { .. }
));

let code = r#"
module ModuleC (
i_sel: input logic<2>,
i_a : input logic<3>,
o_b : output logic,
) {
let c: logic<2> = 2'd1;
always_comb {
case i_sel {
0..=c : o_b = i_a[0];
default: o_b = i_a[1];
}
}
}
"#;

let errors = analyze(code);
assert!(matches!(
errors[0],
AnalyzerError::InvalidCaseConditionNonElaborative { .. }
));

let code = r#"
module ModuleD (
i_sel: input logic<2>,
i_a : input logic<3>,
o_b : output logic,
) {
let c: logic<2> = 2'd0;
assign o_b = case i_sel {
c : i_a[0],
default: i_a[1],
};
}
"#;

let errors = analyze(code);
assert!(matches!(
errors[0],
AnalyzerError::InvalidCaseConditionNonElaborative { .. }
));

let code = r#"
module ModuleE (
i_sel: input logic<2>,
i_a : input logic<3>,
o_b : output logic,
) {
let c: logic<2> = 2'd1;
assign o_b = case i_sel {
0..=c : i_a[0],
default: i_a[1],
};
}
"#;

let errors = analyze(code);
assert!(matches!(
errors[0],
AnalyzerError::InvalidCaseConditionNonElaborative { .. }
));
}
36 changes: 32 additions & 4 deletions crates/emitter/src/aligner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,12 +660,12 @@ impl VerylWalker for Aligner {
fn case_item(&mut self, arg: &CaseItem) {
self.aligns[align_kind::EXPRESSION].start_item();
match &*arg.case_item_group {
CaseItemGroup::ExpressionCaseItemGroupList(x) => {
self.expression(&x.expression);
for x in &x.case_item_group_list {
CaseItemGroup::CaseCondition(x) => {
self.range_item(&x.case_condition.range_item);
for x in &x.case_condition.case_condition_list {
self.comma(&x.comma);
self.space(1);
self.expression(&x.expression);
self.range_item(&x.range_item);
}
}
CaseItemGroup::Defaul(x) => self.defaul(&x.defaul),
Expand All @@ -684,6 +684,34 @@ impl VerylWalker for Aligner {
}
}

/// Semantic action for non-terminal 'SwitchItem'
fn switch_item(&mut self, arg: &SwitchItem) {
self.aligns[align_kind::EXPRESSION].start_item();
match &*arg.switch_item_group {
SwitchItemGroup::SwitchCondition(x) => {
self.expression(&x.switch_condition.expression);
for x in &x.switch_condition.switch_condition_list {
self.comma(&x.comma);
self.space(1);
self.expression(&x.expression);
}
}
SwitchItemGroup::Defaul(x) => self.defaul(&x.defaul),
}
self.aligns[align_kind::EXPRESSION].finish_item();
self.colon(&arg.colon);
match &*arg.switch_item_group0 {
SwitchItemGroup0::Statement(x) => self.statement(&x.statement),
SwitchItemGroup0::LBraceSwitchItemGroup0ListRBrace(x) => {
self.l_brace(&x.l_brace);
for x in &x.switch_item_group0_list {
self.statement(&x.statement);
}
self.r_brace(&x.r_brace);
}
}
}

/// Semantic action for non-terminal 'LetDeclaration'
fn let_declaration(&mut self, arg: &LetDeclaration) {
self.r#let(&arg.r#let);
Expand Down
Loading

0 comments on commit 7c483e6

Please sign in to comment.