Merge pull request #1424 from larsclausen/type-id-parameter-declarations

Support shadowing type identifiers in parameter declarations
This commit is contained in:
Cary R. 2026-07-03 07:14:53 -07:00 committed by GitHub
commit 60a81493cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 334 additions and 53 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -392,6 +392,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

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_type_identifier_parameter_decl_name.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_type_identifier_parameter_port_name.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_type_identifier_parameter_type_decl_name.v",
"iverilog-args" : [ "-g2005-sv" ]
}

View File

@ -0,0 +1,5 @@
{
"type" : "normal",
"source" : "sv_type_identifier_parameter_type_param_name.v",
"iverilog-args" : [ "-g2005-sv" ]
}

168
parse.y
View File

@ -139,12 +139,16 @@ static data_type_t *pform_make_parray_type(const struct vlltype&loc,
template <class T>
static void set_type_id_range(T&value, data_type_t *type, char *id,
unsigned lexical_pos,
const YYLTYPE&loc,
std::list<pform_range_t> *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<pform_range_t>*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<pform_range_t>*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 <class T> void append(vector<T>&out, const std::vector<T>&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<pform_range_t>*ranges;
} type_id_range;
@ -2721,25 +2752,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;
}
;
@ -2749,19 +2781,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);
}
;
@ -2772,7 +2804,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);
@ -2780,7 +2812,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);
}
;
@ -5202,38 +5234,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 "
| 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");
param_data_type = $3;
param_is_type = false;
param_type_restrict = {};
}
}
parameter_assign
| module_parameter_port_list ',' type_param parameter_assign
;
@ -5847,19 +5868,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; }
@ -5871,7 +5879,7 @@ localparam
;
parameter_declaration
: parameter_or_localparam param_type parameter_assign_list ';'
: parameter_or_localparam parameter_assign_list_with_type ';'
parameter_or_localparam
: parameter
@ -5883,17 +5891,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;
}
;