From e56c93a2be56d0459dd36c1670b8d6917ad6c81f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 19:23:11 -0700 Subject: [PATCH 1/2] Support shadowing type identifiers in parameter declarations SystemVerilog allows a visible type identifier to be shadowed by a parameter declaration name. Parameter declarations still required the parameter name to be an `IDENTIFIER` token and rejected declarations like: typedef int P; module test; parameter int P = 1; endmodule The parameter grammar can not just accept `TYPE_IDENTIFIER` in every name position. After `parameter P` the parser does not know yet whether `P` is the parameter name, or whether a following identifier will make `P` the parameter type. Parse the optional value parameter type and the first parameter assignment together. This allows a `TYPE_IDENTIFIER` token to be interpreted as the parameter name when no explicit type is present, while still parsing a following identifier as the parameter name for typed parameters. Signed-off-by: Lars-Peter Clausen --- parse.y | 170 ++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 53 deletions(-) diff --git a/parse.y b/parse.y index 1c9caf208..3fc434bcd 100644 --- a/parse.y +++ b/parse.y @@ -139,12 +139,16 @@ static data_type_t *pform_make_parray_type(const struct vlltype&loc, template static void set_type_id_range(T&value, data_type_t *type, char *id, - unsigned lexical_pos, + const YYLTYPE&loc, std::list *ranges) { value.type = type; value.id = id; - value.lexical_pos = lexical_pos; + value.lexical_pos = loc.lexical_pos; + value.first_line = loc.first_line; + value.first_column = loc.first_column; + value.last_line = loc.last_line; + value.last_column = loc.last_column; value.ranges = ranges; } @@ -299,6 +303,29 @@ static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, return pform_make_net_decl(loc, id, loc.lexical_pos, udims, init); } +static void pform_set_parameter(const YYLTYPE&loc, char *id, unsigned lexical_pos, + bool is_local, bool is_type, + type_restrict_t type_restrict, + data_type_t*data_type, const list*udims, + PExpr*expr, LexicalScope::range_t*value_range) +{ + YYLTYPE id_loc = loc; + id_loc.lexical_pos = lexical_pos; + pform_set_parameter(id_loc, lex_strings.make(id), is_local, is_type, + type_restrict, data_type, udims, expr, value_range); + delete[] id; +} + +static void pform_set_parameter(const YYLTYPE&loc, char *id, + bool is_local, bool is_type, + type_restrict_t type_restrict, + data_type_t*data_type, const list*udims, + PExpr*expr, LexicalScope::range_t*value_range) +{ + pform_set_parameter(loc, id, loc.lexical_pos, is_local, is_type, + type_restrict, data_type, udims, expr, value_range); +} + template void append(vector&out, const std::vector&in) { for (size_t idx = 0 ; idx < in.size() ; idx += 1) @@ -699,6 +726,10 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, data_type_t *type; char *id; unsigned lexical_pos; + int first_line; + int first_column; + int last_line; + int last_column; std::list*ranges; } type_id_range; @@ -2726,25 +2757,26 @@ tf_port_declaration /* IEEE1800-2005: A.2.7 */ // dimensions followed by a separate declaration name. data_type_or_implicit_plus_id_base : IDENTIFIER - { set_type_id_range($$, nullptr, $1, @1.lexical_pos, nullptr); + { set_type_id_range($$, nullptr, $1, @1, nullptr); } | atomic_type identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } | implicit_type identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } | ps_type_identifier_dim identifier_name - { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + { set_type_id_range($$, $1, $2, @2, nullptr); } ; data_type_or_implicit_plus_id_dim : TYPE_IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1.text, @1, $2); } | data_type_or_implicit_plus_id_base dimensions_opt - { set_type_id_range($$, $1.type, $1.id, $1.lexical_pos, $2); + { $$ = $1; + $$.ranges = $2; } ; @@ -2754,19 +2786,19 @@ data_type_or_implicit_plus_id_dim // still parsed as a continuation of the previous port declaration. partial_port_type_plus_id_dim : atomic_type identifier_name dimensions_opt - { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + { set_type_id_range($$, $1, $2, @2, $3); } | implicit_type identifier_name dimensions_opt - { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + { set_type_id_range($$, $1, $2, @2, $3); } ; partial_port_name_dim : IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1, @1, $2); } | TYPE_IDENTIFIER dimensions_opt - { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + { set_type_id_range($$, nullptr, $1.text, @1, $2); } ; @@ -2777,7 +2809,7 @@ partial_port_typedef_plus_id_dim FILE_NAME(tmp, @1); delete[]$1.text; tmp = pform_make_parray_type(@2, tmp, $2); - set_type_id_range($$, tmp, $3, @3.lexical_pos, $4); + set_type_id_range($$, tmp, $3, @3, $4); } | package_scope TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt { lex_in_package_scope(nullptr); @@ -2785,7 +2817,7 @@ partial_port_typedef_plus_id_dim FILE_NAME(tmp, @2); delete[]$2.text; tmp = pform_make_parray_type(@3, tmp, $3); - set_type_id_range($$, tmp, $4, @4.lexical_pos, $5); + set_type_id_range($$, tmp, $4, @4, $5); } ; @@ -5207,38 +5239,27 @@ type_param ; module_parameter - : parameter param_type parameter_assign - | localparam param_type parameter_assign + : parameter parameter_assign_with_type + | localparam parameter_assign_with_type { pform_requires_sv(@1, "Local parameter in module parameter port list"); } ; module_parameter_port_list : module_parameter - | data_type_opt - { param_data_type = $1; - param_is_local = false; - param_is_type = false; - param_type_restrict = {}; - } - parameter_assign - { pform_requires_sv(@3, "Omitting initial `parameter` in parameter port " + | value_parameter_assign_with_type + { pform_requires_sv(@1, "Omitting initial `parameter` in parameter port " "list"); } | type_param { param_is_local = false; } parameter_assign | module_parameter_port_list ',' module_parameter - | module_parameter_port_list ',' data_type_opt - { if ($3) { - pform_requires_sv(@3, "Omitting `parameter`/`localparam` before " - "data type in parameter port list"); - param_data_type = $3; - param_is_type = false; - param_type_restrict = {}; - } + | module_parameter_port_list ',' parameter_assign + | module_parameter_port_list ',' value_parameter_assign_with_explicit_type + { pform_requires_sv(@3, "Omitting `parameter`/`localparam` before " + "data type in parameter port list"); } - parameter_assign | module_parameter_port_list ',' type_param parameter_assign ; @@ -5852,19 +5873,6 @@ net_type_or_var_opt | K_var { $$ = NetNet::REG; } ; - /* The param_type rule is just the data_type_or_implicit rule wrapped - with an assignment to para_data_type with the figured data type. - This is used by parameter_assign, which is found to the right of - the param_type in various rules. */ - -param_type - : data_type_or_implicit - { param_is_type = false; - param_data_type = $1; - param_type_restrict = {}; - } - | type_param - parameter : K_parameter { param_is_local = false; } @@ -5876,7 +5884,7 @@ localparam ; parameter_declaration - : parameter_or_localparam param_type parameter_assign_list ';' + : parameter_or_localparam parameter_assign_list_with_type ';' parameter_or_localparam : parameter @@ -5888,17 +5896,73 @@ parameter_or_localparam handling code. localparams parse the same as parameters, they just behave differently when someone tries to override them. */ -parameter_assign_list - : parameter_assign - | parameter_assign_list ',' parameter_assign + // The first parameter assignment is parsed together with the optional type + // so `parameter T` can shadow a typedef name, while `parameter T p` still + // parses T as the parameter type. Continuations keep the previous type, so a + // bare typedef name after a comma is still a parameter name. +parameter_assign_list_with_type + : parameter_assign_with_type + | parameter_assign_list_with_type ',' parameter_assign + ; + +parameter_assign_with_type + : value_parameter_assign_with_type + | type_param parameter_assign + ; + +value_parameter_assign_with_type + : data_type_or_implicit_plus_id_dim initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1.type; + YYLTYPE id_loc = @1; + id_loc.first_line = $1.first_line; + id_loc.first_column = $1.first_column; + id_loc.last_line = $1.last_line; + id_loc.last_column = $1.last_column; + id_loc.lexical_pos = $1.lexical_pos; + pform_set_parameter(id_loc, $1.id, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $1.ranges, $2, $3); + } + ; + + // Parameter port lists allow an explicit type after a comma without + // repeating `parameter`, e.g. `#(parameter p = 1, int q = 2)`. Keep this + // narrower than value_parameter_assign_with_type so a bare typedef name + // after a comma remains a parameter name that inherits the previous type. +value_parameter_assign_with_explicit_type + : atomic_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } + | implicit_type identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } + | ps_type_identifier_dim identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { param_is_type = false; + param_type_restrict = {}; + param_data_type = $1; + pform_set_parameter(@2, $2, param_is_local, + param_is_type, param_type_restrict, + param_data_type, $3, $4, $5); + } ; parameter_assign - : IDENTIFIER dimensions_opt initializer_opt parameter_value_ranges_opt - { pform_set_parameter(@1, lex_strings.make($1), param_is_local, + : identifier_name dimensions_opt initializer_opt parameter_value_ranges_opt + { pform_set_parameter(@1, $1, param_is_local, param_is_type, param_type_restrict, param_data_type, $2, $3, $4); - delete[]$1; } ; From bc6d421ff26501496f106848757f57a3bcada2cb Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Fri, 26 Jun 2026 19:24:30 -0700 Subject: [PATCH 2/2] Add regression tests for parameter declarations shadowing type identifiers Check that visible type identifiers can be shadowed by value parameter names and by type parameter names. Cover ordinary parameter declarations, typed parameter declarations, and parameter port list declarations separately. Signed-off-by: Lars-Peter Clausen --- .../sv_type_identifier_parameter_decl_name.v | 35 ++++++++ .../sv_type_identifier_parameter_port_name.v | 85 +++++++++++++++++++ ...type_identifier_parameter_type_decl_name.v | 41 +++++++++ ...ype_identifier_parameter_type_param_name.v | 32 +++++++ ivtest/regress-vvp.list | 4 + ...v_type_identifier_parameter_decl_name.json | 5 ++ ...v_type_identifier_parameter_port_name.json | 5 ++ ...e_identifier_parameter_type_decl_name.json | 5 ++ ..._identifier_parameter_type_param_name.json | 5 ++ 9 files changed, 217 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_decl_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_port_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json diff --git a/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v b/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v new file mode 100644 index 000000000..f303cfcf1 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_decl_name.v @@ -0,0 +1,35 @@ +// Check that parameter declaration names can shadow visible type identifiers. + +typedef int L; +typedef int P; +typedef int Q; +typedef int R; + +module test; + + reg failed; + + parameter P = 7, R = 13; + localparam Q = 11, L = 17; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(P, 7, "parameter name did not hide typedef"); + `check(R, 13, "parameter list continuation did not hide typedef"); + `check(Q, 11, "localparam name did not hide typedef"); + `check(L, 17, "localparam list continuation did not hide typedef"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_port_name.v b/ivtest/ivltests/sv_type_identifier_parameter_port_name.v new file mode 100644 index 000000000..76429bebd --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_port_name.v @@ -0,0 +1,85 @@ +// Check that parameter port declaration names can shadow visible type identifiers. + +typedef int P; +typedef int Q; +typedef int R; +typedef logic [7:0] T; +typedef int TP; + +package p; + typedef logic [5:0] PT; +endpackage + +`define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + +module M #( + parameter int P = 5, + Q = 9, + int R = 13, + T typed_value = 8'ha5, + T T = 8'h3c, + p::PT pkg_value = 6'h2a, + parameter type TP = logic [5:0] +) (output reg failed); + + TP type_param_value; + + initial begin + failed = 1'b0; + type_param_value = 6'h15; + + `check(P, 5, "parameter port typed value mismatch"); + `check(Q, 9, "parameter port untyped continuation mismatch"); + `check(R, 13, "parameter port atomic type continuation mismatch"); + `check($bits(typed_value), 8, "parameter port typedef type continuation width mismatch"); + `check(typed_value, 8'ha5, "parameter port typedef type continuation value mismatch"); + `check($bits(T), 8, "parameter port type-name continuation did not keep typedef type"); + `check(T, 8'h3c, "parameter port type-name continuation value mismatch"); + `check($bits(pkg_value), 6, "parameter port package type continuation width mismatch"); + `check(pkg_value, 6'h2a, "parameter port package type continuation value mismatch"); + `check($bits(type_param_value), 6, "parameter port type parameter mismatch"); + `check(type_param_value, 6'h15, "parameter port type parameter value mismatch"); + end + +endmodule + +module N #(P = 3, T typed_value = 8'h5a) (output reg failed); + + initial begin + failed = 1'b0; + + `check(P, 3, "omitted parameter keyword mismatch"); + `check($bits(typed_value), 8, "omitted parameter keyword typedef width mismatch"); + `check(typed_value, 8'h5a, "omitted parameter keyword typedef value mismatch"); + end + +endmodule + +module test; + + reg failed; + wire failed_m; + wire failed_n; + + M i_m(failed_m); + N i_n(failed_n); + + initial begin + failed = 1'b0; + + #1; + + `check(failed_m, 1'b0, "parameter port module failed"); + `check(failed_n, 1'b0, "omitted parameter keyword module failed"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v b/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v new file mode 100644 index 000000000..857dff533 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_type_decl_name.v @@ -0,0 +1,41 @@ +// Check that typed parameter declaration names can shadow visible type identifiers. + +typedef int P; +typedef logic [7:0] T; +typedef logic [6:0] U; + +module test; + + reg failed; + + parameter int P = 13; + parameter T typed_value = 8'ha5; + parameter T T = 8'h3c; + parameter U u0 = 7'h2a, U = 7'h15; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + + `check(P, 13, "typed parameter name did not hide typedef"); + `check($bits(typed_value), 8, "typed parameter width mismatch"); + `check(typed_value, 8'ha5, "typed parameter value mismatch"); + `check($bits(T), 8, "type-name parameter did not keep typedef type"); + `check(T, 8'h3c, "type-name parameter value mismatch"); + `check($bits(u0), 7, "parameter list first declaration did not keep typedef type"); + `check(u0, 7'h2a, "parameter list first value mismatch"); + `check($bits(U), 7, "parameter list continuation did not allow typedef name as parameter name"); + `check(U, 7'h15, "parameter list continuation value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v b/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v new file mode 100644 index 000000000..2494d7b48 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_parameter_type_param_name.v @@ -0,0 +1,32 @@ +// Check that type parameter declaration names can shadow visible type identifiers. + +typedef int TP; + +module test; + + reg failed; + + parameter type TP = logic [3:0]; + + TP type_param_value; + + `define check(value, expected, error) \ + if ((value) !== (expected)) begin \ + $display("FAILED(%0d). %s", `__LINE__, error); \ + $display(" expected %0h, got %0h", expected, value); \ + failed = 1'b1; \ + end + + initial begin + failed = 1'b0; + type_param_value = 4'hc; + + `check($bits(type_param_value), 4, "type parameter name did not hide typedef"); + `check(type_param_value, 4'hc, "type parameter value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 050529a95..f7ed30002 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -389,6 +389,10 @@ sv_type_identifier_modport_name vvp_tests/sv_type_identifier_modport_name.json sv_type_identifier_module_name vvp_tests/sv_type_identifier_module_name.json sv_type_identifier_net_name vvp_tests/sv_type_identifier_net_name.json sv_type_identifier_package_item vvp_tests/sv_type_identifier_package_item.json +sv_type_identifier_parameter_decl_name vvp_tests/sv_type_identifier_parameter_decl_name.json +sv_type_identifier_parameter_port_name vvp_tests/sv_type_identifier_parameter_port_name.json +sv_type_identifier_parameter_type_decl_name vvp_tests/sv_type_identifier_parameter_type_decl_name.json +sv_type_identifier_parameter_type_param_name vvp_tests/sv_type_identifier_parameter_type_param_name.json sv_type_identifier_port_name vvp_tests/sv_type_identifier_port_name.json sv_type_identifier_specparam_name vvp_tests/sv_type_identifier_specparam_name.json sv_type_identifier_task_function_argument_name vvp_tests/sv_type_identifier_task_function_argument_name.json diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json new file mode 100644 index 000000000..9f265426c --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_decl_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_decl_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json new file mode 100644 index 000000000..91cf1e3aa --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_port_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_port_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json new file mode 100644 index 000000000..d51f1988e --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_type_decl_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_type_decl_name.v", + "iverilog-args" : [ "-g2005-sv" ] +} diff --git a/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json b/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json new file mode 100644 index 000000000..fe48a962a --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_parameter_type_param_name.json @@ -0,0 +1,5 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_parameter_type_param_name.v", + "iverilog-args" : [ "-g2005-sv" ] +}