From dc66f2fe7e1fc0ac799c84bd3e911f52a3c3212b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 10:01:43 -0700 Subject: [PATCH 1/2] Support class properties shadowing type names SystemVerilog allows a class property to have the same name as a visible type. The lexer reports the visible type name as `TYPE_IDENTIFIER` before the property has been installed, which made constructs such as `typedef int T; class C; int T; endclass` fail in the class item grammar. A class property with the same name as the class itself hits the same problem. Member references such as `obj.T` or `obj.C` can also hit the same tokenization problem in hierarchical names. Parse class properties through the same declaration helper used for variables so the first type/name pair can be disambiguated. Also let hierarchical member names use `identifier_name`. Stop type lookup when a class scope already has a property with the same name. This makes method body references resolve as properties instead of visible types, including type names found through wildcard imports. Signed-off-by: Lars-Peter Clausen --- parse.y | 15 +++++---------- pform.cc | 10 ++++++++++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/parse.y b/parse.y index 1c9caf208..35e6c89fb 100644 --- a/parse.y +++ b/parse.y @@ -1172,11 +1172,11 @@ class_item /* IEEE1800-2005: A.1.8 */ /* IEEE1800-2017: A.1.9 Class items: Class properties... */ - | property_qualifier_opt data_type list_of_variable_decl_assignments ';' - { pform_class_property(@2, $1, $2, $3); } + | property_qualifier_opt list_of_variable_decl_assignments_with_type ';' + { pform_class_property(@2, $1, $2.type, $2.decl_assignments); } - | K_const class_item_qualifier_opt data_type list_of_variable_decl_assignments ';' - { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3, $4); } + | K_const class_item_qualifier_opt list_of_variable_decl_assignments_with_type ';' + { pform_class_property(@1, $2 | property_qualifier_t::make_const(), $3.type, $3.decl_assignments); } /* IEEEE1800-2017: A.1.9 Class items: class_item ::= { property_qualifier} data_declaration */ @@ -1212,11 +1212,6 @@ class_item /* IEEE1800-2005: A.1.8 */ /* Here are some error matching rules to help recover from various syntax errors within a class declaration. */ - | property_qualifier_opt data_type error ';' - { yyerror(@3, "error: Errors in variable names after data type."); - yyerrok; - } - | property_qualifier_opt IDENTIFIER error ';' { yyerror(@3, "error: %s doesn't name a type.", $2); yyerrok; @@ -4749,7 +4744,7 @@ hierarchy_identifier $$->push_back(name_component_t(lex_strings.make($1))); delete[]$1; } - | hierarchy_identifier '.' IDENTIFIER + | hierarchy_identifier '.' identifier_name { pform_name_t * tmp = $1; tmp->push_back(name_component_t(lex_strings.make($3))); delete[]$3; diff --git a/pform.cc b/pform.cc index cf2f730ce..7aead1052 100644 --- a/pform.cc +++ b/pform.cc @@ -929,6 +929,16 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) if (sym != cur_scope->local_symbols.end()) return nullptr; + // Class properties are tracked in the class type, not in + // local_symbols, but still shadow type names before lookup falls + // through to wildcard imports. + if (auto cur_class = dynamic_cast (cur_scope)) { + if (cur_class->type && + cur_class->type->properties.find(name) != + cur_class->type->properties.end()) + return nullptr; + } + PPackage*pkg = pform_find_potential_import(loc, cur_scope, name, false, false); if (pkg) { cur = pkg->typedefs.find(name); From 5364f11d1640fad6d5de9723db31050d13e8d965 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 27 Jun 2026 10:01:57 -0700 Subject: [PATCH 2/2] Add regression tests for class properties shadowing type names Check that a class property can have the same name as a type declared in an outer scope, or a type imported through a wildcard import. Also check that a class property can have the same name as the class itself. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_class_prop_class_name.v | 37 +++++++++++++++ ivtest/ivltests/sv_class_prop_type_name.v | 39 ++++++++++++++++ .../sv_class_prop_wildcard_type_name.v | 45 +++++++++++++++++++ ivtest/regress-vvp.list | 3 ++ .../vvp_tests/sv_class_prop_class_name.json | 9 ++++ ivtest/vvp_tests/sv_class_prop_type_name.json | 9 ++++ .../sv_class_prop_wildcard_type_name.json | 9 ++++ 7 files changed, 151 insertions(+) create mode 100644 ivtest/ivltests/sv_class_prop_class_name.v create mode 100644 ivtest/ivltests/sv_class_prop_type_name.v create mode 100644 ivtest/ivltests/sv_class_prop_wildcard_type_name.v create mode 100644 ivtest/vvp_tests/sv_class_prop_class_name.json create mode 100644 ivtest/vvp_tests/sv_class_prop_type_name.json create mode 100644 ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json diff --git a/ivtest/ivltests/sv_class_prop_class_name.v b/ivtest/ivltests/sv_class_prop_class_name.v new file mode 100644 index 000000000..2a8c768f7 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_class_name.v @@ -0,0 +1,37 @@ +// Check that a class property can have the same name as the class. + +module test; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int C; + + function int get; + return C; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.C = 23; + + `check(obj.get(), 23); + `check(obj.C, 23); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_class_prop_type_name.v b/ivtest/ivltests/sv_class_prop_type_name.v new file mode 100644 index 000000000..2ba13dea3 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_type_name.v @@ -0,0 +1,39 @@ +// Check that a class property can have the same name as an outer type. + +module test; + + typedef int T; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int T; + + function int get; + return T; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.T = 11; + + `check(obj.get(), 11); + `check(obj.T, 11); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_class_prop_wildcard_type_name.v b/ivtest/ivltests/sv_class_prop_wildcard_type_name.v new file mode 100644 index 000000000..f5a4a1762 --- /dev/null +++ b/ivtest/ivltests/sv_class_prop_wildcard_type_name.v @@ -0,0 +1,45 @@ +// Check that a class property can shadow a wildcard-imported type name. + +package P; + + typedef int T; + +endpackage + +module test; + + import P::*; + + bit failed = 1'b0; + + `define check(val, exp) do \ + if (val !== exp) begin \ + $display("FAILED(%0d). '%s' expected %0d, got %0d", `__LINE__, `"val`", exp, val); \ + failed = 1'b1; \ + end \ + while(0) + + class C; + int T; + + function int get; + return T; + endfunction + endclass + + C obj; + + initial begin + obj = new; + + obj.T = 47; + + `check(obj.get(), 47); + `check(obj.T, 47); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 050529a95..c023763ad 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -279,8 +279,11 @@ sv_chained_constructor4 vvp_tests/sv_chained_constructor4.json sv_chained_constructor5 vvp_tests/sv_chained_constructor5.json sv_class_prop_assign_op1 vvp_tests/sv_class_prop_assign_op1.json sv_class_prop_assign_op2 vvp_tests/sv_class_prop_assign_op2.json +sv_class_prop_class_name vvp_tests/sv_class_prop_class_name.json sv_class_prop_logic vvp_tests/sv_class_prop_logic.json sv_class_prop_packed_dims vvp_tests/sv_class_prop_packed_dims.json +sv_class_prop_type_name vvp_tests/sv_class_prop_type_name.json +sv_class_prop_wildcard_type_name vvp_tests/sv_class_prop_wildcard_type_name.json sv_class_prop_nest_darray1 vvp_tests/sv_class_prop_nest_darray1.json sv_class_prop_nest_obj1 vvp_tests/sv_class_prop_nest_obj1.json sv_class_prop_nest_real1 vvp_tests/sv_class_prop_nest_real1.json diff --git a/ivtest/vvp_tests/sv_class_prop_class_name.json b/ivtest/vvp_tests/sv_class_prop_class_name.json new file mode 100644 index 000000000..4c7cfe9c8 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_class_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_class_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_prop_type_name.json b/ivtest/vvp_tests/sv_class_prop_type_name.json new file mode 100644 index 000000000..e5543bb21 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_type_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_type_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json b/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json new file mode 100644 index 000000000..451678584 --- /dev/null +++ b/ivtest/vvp_tests/sv_class_prop_wildcard_type_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_class_prop_wildcard_type_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Classes are not supported", + "type" : "CE" + } +}