Skip to content

Commit 4937a7e

Browse files
authored
Merge branch 'master' into feat/memory-ssa-comb-analysis
2 parents e028557 + ee52384 commit 4937a7e

5 files changed

Lines changed: 337 additions & 44 deletions

File tree

crates/analyzer/src/handlers/check_attribute.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,15 @@ impl CheckAttribute {
165165
self.check_ifdef(&mut attrs, last);
166166
self.set_attrs(attrs, range);
167167
}
168+
169+
/// An elsif/else can only chain to an ifdef/ifndef in the same group
170+
/// list; clear the state left by a previous list or an enclosing scope,
171+
/// which would otherwise let an orphan #[else] emit an unmatched `else.
172+
fn reset_ifdef(&mut self) {
173+
self.ifdef_state = IfdefState::None;
174+
self.ifdef_pos.clear();
175+
self.ifdef_neg.clear();
176+
}
168177
}
169178

170179
impl Handler for CheckAttribute {
@@ -176,6 +185,7 @@ impl Handler for CheckAttribute {
176185
impl VerylGrammarTrait for CheckAttribute {
177186
fn statement_block(&mut self, arg: &StatementBlock) -> Result<(), ParolError> {
178187
if let HandlerPoint::Before = self.point {
188+
self.reset_ifdef();
179189
for x in &arg.statement_block_list {
180190
let x = x.statement_block_group.as_ref();
181191
let attrs: Vec<_> = x
@@ -191,6 +201,7 @@ impl VerylGrammarTrait for CheckAttribute {
191201

192202
fn modport_list(&mut self, arg: &ModportList) -> Result<(), ParolError> {
193203
if let HandlerPoint::Before = self.point {
204+
self.reset_ifdef();
194205
let mut groups = vec![arg.modport_group.as_ref()];
195206
groups.extend(
196207
arg.modport_list_list
@@ -213,6 +224,7 @@ impl VerylGrammarTrait for CheckAttribute {
213224

214225
fn enum_list(&mut self, arg: &EnumList) -> Result<(), ParolError> {
215226
if let HandlerPoint::Before = self.point {
227+
self.reset_ifdef();
216228
let mut groups = vec![arg.enum_group.as_ref()];
217229
groups.extend(arg.enum_list_list.iter().map(|x| x.enum_group.as_ref()));
218230
let len = groups.len();
@@ -231,6 +243,7 @@ impl VerylGrammarTrait for CheckAttribute {
231243

232244
fn struct_union_list(&mut self, arg: &StructUnionList) -> Result<(), ParolError> {
233245
if let HandlerPoint::Before = self.point {
246+
self.reset_ifdef();
234247
let mut groups = vec![arg.struct_union_group.as_ref()];
235248
groups.extend(
236249
arg.struct_union_list_list
@@ -253,6 +266,7 @@ impl VerylGrammarTrait for CheckAttribute {
253266

254267
fn inst_parameter_list(&mut self, arg: &InstParameterList) -> Result<(), ParolError> {
255268
if let HandlerPoint::Before = self.point {
269+
self.reset_ifdef();
256270
let mut groups = vec![arg.inst_parameter_group.as_ref()];
257271
groups.extend(
258272
arg.inst_parameter_list_list
@@ -275,6 +289,7 @@ impl VerylGrammarTrait for CheckAttribute {
275289

276290
fn inst_port_list(&mut self, arg: &InstPortList) -> Result<(), ParolError> {
277291
if let HandlerPoint::Before = self.point {
292+
self.reset_ifdef();
278293
let mut groups = vec![arg.inst_port_group.as_ref()];
279294
groups.extend(
280295
arg.inst_port_list_list
@@ -297,6 +312,7 @@ impl VerylGrammarTrait for CheckAttribute {
297312

298313
fn with_parameter_list(&mut self, arg: &WithParameterList) -> Result<(), ParolError> {
299314
if let HandlerPoint::Before = self.point {
315+
self.reset_ifdef();
300316
let mut groups = vec![arg.with_parameter_group.as_ref()];
301317
groups.extend(
302318
arg.with_parameter_list_list
@@ -319,6 +335,7 @@ impl VerylGrammarTrait for CheckAttribute {
319335

320336
fn port_declaration_list(&mut self, arg: &PortDeclarationList) -> Result<(), ParolError> {
321337
if let HandlerPoint::Before = self.point {
338+
self.reset_ifdef();
322339
let mut groups = vec![arg.port_declaration_group.as_ref()];
323340
groups.extend(
324341
arg.port_declaration_list_list
@@ -341,6 +358,7 @@ impl VerylGrammarTrait for CheckAttribute {
341358

342359
fn module_declaration(&mut self, arg: &ModuleDeclaration) -> Result<(), ParolError> {
343360
if let HandlerPoint::Before = self.point {
361+
self.reset_ifdef();
344362
for x in &arg.module_declaration_list {
345363
let x = x.module_group.as_ref();
346364
let attrs: Vec<_> = x
@@ -356,6 +374,7 @@ impl VerylGrammarTrait for CheckAttribute {
356374

357375
fn interface_declaration(&mut self, arg: &InterfaceDeclaration) -> Result<(), ParolError> {
358376
if let HandlerPoint::Before = self.point {
377+
self.reset_ifdef();
359378
for x in &arg.interface_declaration_list {
360379
let x = x.interface_group.as_ref();
361380
let attrs: Vec<_> = x
@@ -371,6 +390,7 @@ impl VerylGrammarTrait for CheckAttribute {
371390

372391
fn generate_named_block(&mut self, arg: &GenerateNamedBlock) -> Result<(), ParolError> {
373392
if let HandlerPoint::Before = self.point {
393+
self.reset_ifdef();
374394
for x in &arg.generate_named_block_list {
375395
let x = x.generate_group.as_ref();
376396
let attrs: Vec<_> = x
@@ -389,6 +409,7 @@ impl VerylGrammarTrait for CheckAttribute {
389409
arg: &GenerateOptionalNamedBlock,
390410
) -> Result<(), ParolError> {
391411
if let HandlerPoint::Before = self.point {
412+
self.reset_ifdef();
392413
for x in &arg.generate_optional_named_block_list {
393414
let x = x.generate_group.as_ref();
394415
let attrs: Vec<_> = x
@@ -404,6 +425,7 @@ impl VerylGrammarTrait for CheckAttribute {
404425

405426
fn package_declaration(&mut self, arg: &PackageDeclaration) -> Result<(), ParolError> {
406427
if let HandlerPoint::Before = self.point {
428+
self.reset_ifdef();
407429
for x in &arg.package_declaration_list {
408430
let x = x.package_group.as_ref();
409431
let attrs: Vec<_> = x
@@ -419,6 +441,7 @@ impl VerylGrammarTrait for CheckAttribute {
419441

420442
fn veryl(&mut self, arg: &Veryl) -> Result<(), ParolError> {
421443
if let HandlerPoint::Before = self.point {
444+
self.reset_ifdef();
422445
for x in &arg.veryl_list {
423446
let x = x.description_group.as_ref();
424447
let attrs: Vec<_> = x

crates/analyzer/src/symbol_table/enum.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ pub fn resolve_enum(list: &[Symbol]) -> Vec<AnalyzerError> {
2222
let mut pre_value = None;
2323
let mut member_width = 0;
2424
let mut seen_values: HashSet<BigUint> = HashSet::default();
25+
// x/z values have no numeric `value()`, so they bypass `seen_values`.
26+
let mut seen_xz: HashSet<(BigUint, BigUint)> = HashSet::default();
2527
for id in &r#enum.members {
2628
let mut symbol = symbol_table::get(*id).unwrap();
2729

@@ -32,6 +34,7 @@ pub fn resolve_enum(list: &[Symbol]) -> Vec<AnalyzerError> {
3234
pre_value.as_ref(),
3335
enum_width,
3436
&mut member_width,
37+
&mut seen_xz,
3538
&mut errors,
3639
);
3740
if matches!(value, EnumMemberValue::UnevaluableValue) {
@@ -110,13 +113,15 @@ fn eval_enum_width(
110113
width
111114
}
112115

116+
#[allow(clippy::too_many_arguments)]
113117
fn eval_enum_member_value(
114118
context: &mut Context,
115119
symbol: &Symbol,
116120
r#enum: &EnumProperty,
117121
pre_value: Option<&EnumMemberValue>,
118122
enum_width: usize,
119123
member_width: &mut usize,
124+
seen_xz: &mut HashSet<(BigUint, BigUint)>,
120125
errors: &mut Vec<AnalyzerError>,
121126
) -> EnumMemberValue {
122127
let SymbolKind::EnumMember(enum_member) = &symbol.kind else {
@@ -164,6 +169,15 @@ fn eval_enum_member_value(
164169
));
165170
}
166171
*member_width = (*member_width).max(needed);
172+
// Key on (payload, x/z-mask): z sets payload bits under the mask,
173+
// so x and z differ.
174+
let key = (value.payload().into_owned(), value.mask_xz().into_owned());
175+
if !seen_xz.insert(key) {
176+
errors.push(AnalyzerError::duplicate_enum_variant(
177+
&symbol.token.to_string(),
178+
&symbol.token.into(),
179+
));
180+
}
167181
enum_member.value.clone()
168182
}
169183
} else {

crates/analyzer/src/tests.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17062,6 +17062,49 @@ fn non_constant_range_part_select_bounds() {
1706217062
);
1706317063
}
1706417064

17065+
#[test]
17066+
fn enum_xz_duplicate_variant() {
17067+
// Regression: x/z variants have no numeric value(), so they bypassed the
17068+
// duplicate-variant check; identical x/z literals were silently accepted.
17069+
let code = r#"
17070+
module Top {
17071+
enum Foo: logic<4> {
17072+
A = 4'b1x0z,
17073+
B = 4'b1x0z,
17074+
C = 4'b1x0z,
17075+
}
17076+
var _v: Foo;
17077+
assign _v = Foo::A;
17078+
}
17079+
"#;
17080+
let errors = analyze(code);
17081+
assert!(
17082+
errors
17083+
.iter()
17084+
.any(|e| matches!(e, AnalyzerError::DuplicateEnumVariant { .. })),
17085+
"{errors:?}"
17086+
);
17087+
17088+
// x and z at the same position are distinct (z stores payload 1).
17089+
let code = r#"
17090+
module Top {
17091+
enum Foo: logic<4> {
17092+
A = 4'b1x,
17093+
B = 4'b1z,
17094+
}
17095+
var _v: Foo;
17096+
assign _v = Foo::A;
17097+
}
17098+
"#;
17099+
let errors = analyze(code);
17100+
assert!(
17101+
!errors
17102+
.iter()
17103+
.any(|e| matches!(e, AnalyzerError::DuplicateEnumVariant { .. })),
17104+
"{errors:?}"
17105+
);
17106+
}
17107+
1706517108
#[test]
1706617109
fn enum_xz_variant_checks() {
1706717110
// Regression: a variant value containing any x/z bit bypassed the
@@ -24473,3 +24516,66 @@ fn comb_loop_uses_structural_selector_regions() {
2447324516
);
2447424517
}
2447524518
}
24519+
fn orphan_else_across_scopes() {
24520+
// The ifdef/elsif/else attribute state persisted across attribute-group
24521+
// lists and nesting levels, so an orphan #[else] chained to an #[ifdef]
24522+
// in a previous module or an enclosing scope and emitted an unmatched
24523+
// `else into the SV output.
24524+
let code = r#"
24525+
module ModuleA {
24526+
#[ifdef(A)]
24527+
let _x: logic = 1'b0;
24528+
let _z: logic = 1'b0;
24529+
}
24530+
module ModuleB {
24531+
#[else]
24532+
let _y: logic = 1'b0;
24533+
let _w: logic = 1'b0;
24534+
}
24535+
"#;
24536+
24537+
let errors = analyze(code);
24538+
assert!(
24539+
errors
24540+
.iter()
24541+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
24542+
"{errors:?}"
24543+
);
24544+
24545+
// Nested scope: an outer #[ifdef] must not legitimize an inner #[else].
24546+
let code = r#"
24547+
#[ifdef(DEF_X)]
24548+
module ModuleA {
24549+
#[else]
24550+
let _a: logic = 1;
24551+
let _b: logic = 1;
24552+
}
24553+
"#;
24554+
24555+
let errors = analyze(code);
24556+
assert!(
24557+
errors
24558+
.iter()
24559+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
24560+
"{errors:?}"
24561+
);
24562+
24563+
// A well-formed chain in one list stays accepted.
24564+
let code = r#"
24565+
module ModuleA {
24566+
#[ifdef(A)]
24567+
let _x: logic = 1'b0;
24568+
#[else]
24569+
let _y: logic = 1'b0;
24570+
let _z: logic = 1'b0;
24571+
}
24572+
"#;
24573+
24574+
let errors = analyze(code);
24575+
assert!(
24576+
!errors
24577+
.iter()
24578+
.any(|e| matches!(e, AnalyzerError::AmbiguousElsif { .. })),
24579+
"{errors:?}"
24580+
);
24581+
}

0 commit comments

Comments
 (0)