From 7cffbf440dbf439718d96b0ac15c7fd1b60a57a9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Jun 2026 23:10:30 -0700 Subject: [PATCH 1/2] Support declaration names shadowing type identifiers SystemVerilog allows a declaration in an inner scope to use the same name as a type identifier from an outer scope. The lexer reports such names as `TYPE_IDENTIFIER` before the new declaration has been installed, which made constructs such as `int T;`, `wire T;`, and `input T` fail when `T` was a visible typedef. The affected declaration forms have a local type/name/dimension ambiguity. For example, after `input T` or `wire T` the parser does not know whether `T` is the declared name, or whether a following identifier will make `T` the declaration type in `input T x` or `wire T x`. With dimensions, `input T [1:0]` and `wire T [1:0]` can be either a declaration named `T` with unpacked dimensions or a declaration using typedef `T` as a packed type followed by another name. Parse these declaration forms with productions that decide the first declarator and carry the selected declaration type across the rest of the list. This covers variable declarations, net declarations, ANSI and non-ANSI module port declarations, and task/function port declarations. Other identifier uses still need separate grammar changes. Signed-off-by: Lars-Peter Clausen --- parse.y | 619 +++++++++++++++++++++++++++++++++++--------------- pform_types.h | 5 + 2 files changed, 442 insertions(+), 182 deletions(-) diff --git a/parse.y b/parse.y index 4b44d0358..1c9caf208 100644 --- a/parse.y +++ b/parse.y @@ -124,6 +124,41 @@ static void check_net_decl_assigns(const struct vlltype&loc, } } +static data_type_t *pform_make_parray_type(const struct vlltype&loc, + data_type_t *base, + std::list *pdims) +{ + if (!pdims) + return base; + + data_type_t *type = new parray_type_t(base, pdims); + FILE_NAME(type, loc); + + return type; +} + +template +static void set_type_id_range(T&value, data_type_t *type, char *id, + unsigned lexical_pos, + std::list *ranges) +{ + value.type = type; + value.id = id; + value.lexical_pos = lexical_pos; + value.ranges = ranges; +} + +template +static void delete_type_id_range(T&value) +{ + delete value.type; + delete[] value.id; + delete value.ranges; + value.type = nullptr; + value.id = nullptr; + value.ranges = nullptr; +} + /* The rules sometimes push attributes into a global context where sub-rules may grab them. This makes parser rules a little easier to write in some cases. */ @@ -169,25 +204,29 @@ static std::list*attributes_in_context = 0; static const struct str_pair_t pull_strength = { IVL_DR_PULL, IVL_DR_PULL }; static const struct str_pair_t str_strength = { IVL_DR_STRONG, IVL_DR_STRONG }; -static std::list* make_port_list(char*id, unsigned idn, - std::list*udims, - PExpr*expr) +static struct pform_port_list make_port_list(data_type_t *type, char*id, + unsigned idn, + std::list*udims, + PExpr*expr) { - std::list*tmp = new std::list; + struct pform_port_list list; + list.type = type; + list.ports = new std::list; pform_ident_t tmp_name = { lex_strings.make(id), idn }; - tmp->push_back(pform_port_t(tmp_name, udims, expr)); + list.ports->push_back(pform_port_t(tmp_name, udims, expr)); delete[]id; - return tmp; + return list; } -static std::list* make_port_list(list*tmp, - char*id, unsigned idn, - std::list*udims, - PExpr*expr) + +static struct pform_port_list make_port_list(struct pform_port_list list, + char*id, unsigned idn, + std::list*udims, + PExpr*expr) { pform_ident_t tmp_name = { lex_strings.make(id), idn }; - tmp->push_back(pform_port_t(tmp_name, udims, expr)); + list.ports->push_back(pform_port_t(tmp_name, udims, expr)); delete[]id; - return tmp; + return list; } static std::list* list_from_identifier(char*id, unsigned idn) @@ -206,6 +245,60 @@ static std::list* list_from_identifier(list*tmp, return tmp; } +static decl_assignment_t *pform_make_var_decl(const YYLTYPE&loc, char *id, + unsigned lexical_pos, + std::list*udims, + PExpr *init) +{ + if (init && pform_peek_scope()->var_init_needs_explicit_lifetime() && + var_lifetime == LexicalScope::INHERITED) { + cerr << loc << ": warning: Static variable initialization requires " + "explicit lifetime in this context." << endl; + warn_count += 1; + } + decl_assignment_t *decl = new decl_assignment_t; + decl->name = { lex_strings.make(id), lexical_pos }; + if (udims) { + decl->index = *udims; + delete udims; + } + decl->expr.reset(init); + delete[] id; + return decl; +} + +static decl_assignment_t *pform_make_var_decl(const YYLTYPE&loc, char *id, + std::list*udims, + PExpr *init) +{ + return pform_make_var_decl(loc, id, loc.lexical_pos, udims, init); +} + +static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, + unsigned lexical_pos, + std::list*udims, + PExpr *init) +{ + decl_assignment_t *decl = new decl_assignment_t; + decl->name = { lex_strings.make(id), lexical_pos }; + if (udims) { + decl->index = *udims; + if (init) + pform_requires_sv(loc, "Assignment of net array during declaration"); + delete udims; + } + decl->expr.reset(init); + delete[] id; + return decl; +} + +static decl_assignment_t *pform_make_net_decl(const YYLTYPE&loc, char *id, + std::list*udims, + PExpr *init) +{ + return pform_make_net_decl(loc, id, loc.lexical_pos, udims, init); +} + template void append(vector&out, const std::vector&in) { for (size_t idx = 0 ; idx < in.size() ; idx += 1) @@ -427,14 +520,15 @@ static void port_declaration_context_init(void) } Module::port_t *module_declare_port(const YYLTYPE&loc, char *id, - NetNet::PortType port_type, + unsigned lexical_pos, + NetNet::PortType port_type, NetNet::Type net_type, data_type_t *data_type, std::list *unpacked_dims, PExpr *default_value, std::list *attributes) { - pform_ident_t name = { lex_strings.make(id), loc.lexical_pos }; + pform_ident_t name = { lex_strings.make(id), lexical_pos }; delete[] id; Module::port_t *port = pform_module_port_reference(loc, name.first); @@ -527,8 +621,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, std::list*perm_strings; std::list*identifiers; - - std::list*port_list; + struct pform_port_list port_list; std::vector* tf_ports; @@ -583,6 +676,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, decl_assignment_t*decl_assignment; std::list*decl_assignments; + struct { + data_type_t *type; + std::list *decl_assignments; + } decl_assignments_with_type; + struct_member_t*struct_member; std::list*struct_members; struct_type_t*struct_type; @@ -597,6 +695,13 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, typedef_t*type; } type_identifier; + struct { + data_type_t *type; + char *id; + unsigned lexical_pos; + std::list*ranges; + } type_id_range; + struct { data_type_t*type; std::list *args; @@ -807,12 +912,15 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type net_decl_assign variable_decl_assignment %type net_decl_assigns list_of_variable_decl_assignments +%type list_of_net_decl_assignments_with_type +%type list_of_variable_decl_assignments_with_type %type data_type data_type_opt data_type_or_implicit data_type_or_implicit_or_void -%type data_type_or_implicit_no_opt -%type simple_type_or_string let_formal_type -%type packed_array_data_type -%type ps_type_identifier +%type implicit_type +%type reg_prefixed_atomic_type simple_type_or_string let_formal_type +%type packed_array_data_type atomic_type + +%type ps_type_identifier ps_type_identifier_dim %type simple_packed_type %type class_scope %type struct_union_member @@ -833,7 +941,7 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type net_type net_type_opt net_type_or_var net_type_or_var_opt %type gatetype switchtype %type port_direction port_direction_opt -%type integer_vector_type +%type integer_vector_type integer_vector_type_no_reg %type parameter_value_opt %type event_expression_list @@ -877,6 +985,11 @@ Module::port_t *module_declare_interface_port(const YYLTYPE&loc, char *type, %type compressed_operator %type forward_type forward_type_without_enum +%type data_type_or_implicit_plus_id_base +%type data_type_or_implicit_plus_id_dim +%type partial_port_name_dim +%type partial_port_type_plus_id_dim +%type partial_port_typedef_plus_id_dim %token K_TAND %nonassoc K_PLUS_EQ K_MINUS_EQ K_MUL_EQ K_DIV_EQ K_MOD_EQ K_AND_EQ K_OR_EQ @@ -1332,13 +1445,13 @@ data_declaration /* IEEE1800-2005: A.2.1.3 */ $1, $2); var_lifetime = LexicalScope::INHERITED; } - | attribute_list_opt K_const_opt K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';' - { data_type_t *data_type = $5; + | attribute_list_opt K_const_opt K_var variable_lifetime_opt list_of_variable_decl_assignments_with_type ';' + { data_type_t*data_type = $5.type; if (!data_type) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); FILE_NAME(data_type, @3); } - pform_make_var(@3, $6, data_type, $1, $2); + pform_make_var(@3, $5.decl_assignments, data_type, $1, $2); var_lifetime = LexicalScope::INHERITED; } | attribute_list_opt K_event event_variable_list ';' @@ -1354,6 +1467,7 @@ package_scope } ; + // Type identifiers with and without attached packed dimensions. ps_type_identifier /* IEEE1800-2017: A.9.3 */ : TYPE_IDENTIFIER { pform_set_type_referenced(@1, $1.text); @@ -1369,17 +1483,33 @@ ps_type_identifier /* IEEE1800-2017: A.9.3 */ } ; +ps_type_identifier_dim /* IEEE1800-2017: A.9.3 */ + : TYPE_IDENTIFIER dimensions_opt + { pform_set_type_referenced(@1, $1.text); + data_type_t*tmp = new typeref_t($1.type); + FILE_NAME(tmp, @1); + delete[]$1.text; + $$ = pform_make_parray_type(@2, tmp, $2); + } + | package_scope TYPE_IDENTIFIER dimensions_opt + { lex_in_package_scope(nullptr); + data_type_t*tmp = new typeref_t($2.type, $1); + FILE_NAME(tmp, @2); + $$ = pform_make_parray_type(@3, tmp, $3); + delete[]$2.text; + } + ; + /* Data types that can have packed dimensions directly attached to it */ packed_array_data_type /* IEEE1800-2005: A.2.2.1 */ - : enum_data_type - { $$ = $1; } - | struct_data_type + : enum_data_type dimensions_opt + { $$ = pform_make_parray_type(@2, $1, $2); } + | struct_data_type dimensions_opt { if (!$1->packed_flag && !($1->union_flag && $1->soft_flag)) { yyerror(@1, "sorry: Unpacked structs not supported."); - } - $$ = $1; + } + $$ = pform_make_parray_type(@2, $1, $2); } - | ps_type_identifier ; simple_packed_type /* Integer and vector types */ @@ -1400,7 +1530,7 @@ simple_packed_type /* Integer and vector types */ } ; -data_type /* IEEE1800-2005: A.2.2.1 */ +atomic_type : simple_packed_type { $$ = $1; } @@ -1409,14 +1539,40 @@ data_type /* IEEE1800-2005: A.2.2.1 */ FILE_NAME(tmp, @1); $$ = tmp; } - | packed_array_data_type dimensions_opt - { if ($2) { - parray_type_t*tmp = new parray_type_t($1, $2); - FILE_NAME(tmp, @1); - $$ = tmp; - } else { - $$ = $1; - } + | packed_array_data_type { $$ = $1; } + | K_string + { string_type_t*tmp = new string_type_t; + FILE_NAME(tmp, @1); + $$ = tmp; + } + ; + +/* Data types allowed after the historical iverilog extension that permits an + extra leading `reg` in block declarations. Keep `reg` itself out of this + subset so ordinary `reg` declarations continue through the normal rules. */ +reg_prefixed_atomic_type + : integer_vector_type_no_reg unsigned_signed_opt dimensions_opt + { vector_type_t*tmp = new vector_type_t($1, $2, $3); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | atom_type signed_unsigned_opt + { atom_type_t*tmp = new atom_type_t($1, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | K_time unsigned_signed_opt + { atom_type_t*tmp = new atom_type_t(atom_type_t::TIME, $2); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | non_integer_type + { real_type_t*tmp = new real_type_t($1); + FILE_NAME(tmp, @1); + $$ = tmp; + } + | packed_array_data_type + { $$ = $1; } | K_string { string_type_t*tmp = new string_type_t; @@ -1425,6 +1581,11 @@ data_type /* IEEE1800-2005: A.2.2.1 */ } ; +data_type /* IEEE1800-2005: A.2.2.1 */ + : atomic_type { $$ = $1; } + | ps_type_identifier_dim { $$ = $1; } + ; + /* Data type or nothing, but not implicit */ data_type_opt : data_type { $$ = $1; } @@ -1436,6 +1597,11 @@ data_type_opt absent. The context may need that information to decide to resort to left context. */ +data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ + : data_type_opt { $$ = $1; } + | implicit_type { $$ = $1; } + ; + scalar_vector_opt /*IEEE1800-2005: optional support for packed array */ : K_vectored { /* Ignore */ } @@ -1445,14 +1611,8 @@ scalar_vector_opt /*IEEE1800-2005: optional support for packed array */ { /* Ignore */ } ; -data_type_or_implicit /* IEEE1800-2005: A.2.2.1 */ - : data_type_or_implicit_no_opt - | { $$ = nullptr; } - -data_type_or_implicit_no_opt - : data_type - { $$ = $1; } - | signing dimensions_opt +implicit_type + : signing dimensions_opt { vector_type_t*tmp = new vector_type_t(IVL_VT_LOGIC, $1, $2); tmp->implicit_flag = true; FILE_NAME(tmp, @1); @@ -1466,7 +1626,6 @@ data_type_or_implicit_no_opt } ; - data_type_or_implicit_or_void : data_type_or_implicit { $$ = $1; } @@ -1772,7 +1931,13 @@ inside_expression /* IEEE1800-2005 A.8.3 */ integer_vector_type /* IEEE1800-2005: A.2.2.1 */ : K_reg { $$ = IVL_VT_LOGIC; } /* A synonym for logic. */ - | K_bit { $$ = IVL_VT_BOOL; } + | integer_vector_type_no_reg + { $$ = $1; + } + ; + +integer_vector_type_no_reg + : K_bit { $$ = IVL_VT_BOOL; } | K_logic { $$ = IVL_VT_LOGIC; } | K_bool { $$ = IVL_VT_BOOL; } /* Icarus Verilog xtypes extension */ ; @@ -1962,6 +2127,18 @@ loop_statement /* IEEE1800-2005: A.6.8 */ } ; +list_of_variable_decl_assignments_with_type /* IEEE1800-2005 A.2.3 */ + : data_type_or_implicit_plus_id_dim var_decl_initializer_opt + { std::list*tmp = new std::list; + tmp->push_back(pform_make_var_decl(@1, $1.id, $1.lexical_pos, $1.ranges, $2)); + $$.decl_assignments = tmp; + $$.type = $1.type; + } + | list_of_variable_decl_assignments_with_type ',' variable_decl_assignment + { $1.decl_assignments->push_back($3); + $$ = $1; + } + ; list_of_variable_decl_assignments /* IEEE1800-2005 A.2.3 */ : variable_decl_assignment @@ -1988,23 +2165,8 @@ var_decl_initializer_opt ; variable_decl_assignment /* IEEE1800-2005 A.2.3 */ - : IDENTIFIER dimensions_opt var_decl_initializer_opt - { if ($3 && pform_peek_scope()->var_init_needs_explicit_lifetime() - && (var_lifetime == LexicalScope::INHERITED)) { - cerr << @1 << ": warning: Static variable initialization requires " - "explicit lifetime in this context." << endl; - warn_count += 1; - } - - decl_assignment_t*tmp = new decl_assignment_t; - tmp->name = { lex_strings.make($1), @1.lexical_pos }; - if ($2) { - tmp->index = *$2; - delete $2; - } - tmp->expr.reset($3); - delete[]$1; - $$ = tmp; + : identifier_name dimensions_opt var_decl_initializer_opt + { $$ = pform_make_var_decl(@1, $1, $2, $3); } ; @@ -2077,7 +2239,7 @@ modport_ports_list | modport_ports_list ',' named_expression { if (last_modport_port.type == MP_SIMPLE) { pform_add_modport_port(@3, last_modport_port.direction, - $3->name, $3->parm); + $3->name, $3->parm); } else { yyerror(@3, "error: modport expression not allowed here."); } @@ -2551,11 +2713,81 @@ task_declaration /* IEEE1800-2005: A.2.7 */ tf_port_declaration /* IEEE1800-2005: A.2.7 */ - : port_direction K_var_opt data_type_or_implicit list_of_port_identifiers ';' - { $$ = pform_make_task_ports(@1, $1, $3, $4, true); + : port_direction K_var_opt list_of_port_identifiers ';' + { $$ = pform_make_task_ports(@1, $1, $3.type, $3.ports, true); } ; + // These rules only disambiguate declaration items that can be either an + // implicit declaration name or an explicit type followed by a name. Keep the + // bare `TYPE_IDENTIFIER dimensions_opt` case in the parent rule so a typedef + // name can be shadowed by an unpacked declaration name, while + // `ps_type_identifier_dim identifier_name` still parses a typedef with packed + // 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); + } + | atomic_type identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + | implicit_type identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + | ps_type_identifier_dim identifier_name + { set_type_id_range($$, $1, $2, @2.lexical_pos, nullptr); + } + ; + +data_type_or_implicit_plus_id_dim + : TYPE_IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + } + | data_type_or_implicit_plus_id_base dimensions_opt + { set_type_id_range($$, $1.type, $1.id, $1.lexical_pos, $2); + } + ; + + // Partial ANSI port declarations such as `input a, integer b` can redeclare + // the data type without repeating the direction. Keep this narrower than + // data_type_or_implicit_plus_id_dim so a bare identifier after a comma is + // 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); + } + | implicit_type identifier_name dimensions_opt + { set_type_id_range($$, $1, $2, @2.lexical_pos, $3); + } + ; + +partial_port_name_dim + : IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1, @1.lexical_pos, $2); + } + | TYPE_IDENTIFIER dimensions_opt + { set_type_id_range($$, nullptr, $1.text, @1.lexical_pos, $2); + } + ; + +partial_port_typedef_plus_id_dim + : TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt + { pform_set_type_referenced(@1, $1.text); + data_type_t*tmp = new typeref_t($1.type); + 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); + } + | package_scope TYPE_IDENTIFIER dimensions_opt identifier_name dimensions_opt + { lex_in_package_scope(nullptr); + data_type_t*tmp = new typeref_t($2.type, $1); + 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); + } + ; /* These rules for tf_port_item are slightly expanded from the strict rules in the LRM to help with LALR parsing. @@ -2567,61 +2799,62 @@ tf_port_declaration /* IEEE1800-2005: A.2.7 */ tf_port_item /* IEEE1800-2005: A.2.7 */ - : port_direction_opt K_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt + : port_direction_opt K_var_opt data_type_or_implicit_plus_id_dim initializer_opt { std::vector*tmp; NetNet::PortType use_port_type = $1; - if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || ($3 == 0))) + if ((use_port_type == NetNet::PIMPLICIT) && (gn_system_verilog() || !$3.type)) use_port_type = port_declaration_context.port_type; - list* port_list = make_port_list($4, @4.lexical_pos, $5, 0); + struct pform_port_list port_list = make_port_list($3.type, $3.id, + $3.lexical_pos, + $3.ranges, nullptr); if (use_port_type == NetNet::PIMPLICIT) { yyerror(@1, "error: Missing task/function port direction."); use_port_type = NetNet::PINPUT; // for error recovery } - if (($3 == 0) && ($1==NetNet::PIMPLICIT)) { + if (!$3.type && ($1==NetNet::PIMPLICIT)) { // Detect special case this is an undecorated // identifier and we need to get the declaration from // left context. - if ($5 != 0) { - yyerror(@5, "internal error: How can there be an unpacked range here?\n"); - } - tmp = pform_make_task_ports(@4, use_port_type, + tmp = pform_make_task_ports(@3, use_port_type, port_declaration_context.data_type, - port_list); + port_list.ports); } else { // Otherwise, the decorations for this identifier // indicate the type. Save the type for any right // context that may come later. port_declaration_context.port_type = use_port_type; - if ($3 == 0) { - $3 = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME($3, @4); + if (!$3.type) { + $3.type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME($3.type, @3); } - port_declaration_context.data_type = $3; - tmp = pform_make_task_ports(@3, use_port_type, $3, port_list); + port_declaration_context.data_type = $3.type; + tmp = pform_make_task_ports(@3, use_port_type, $3.type, + port_list.ports); } $$ = tmp; - if ($6) { - pform_requires_sv(@6, "Task/function default argument"); + if ($4) { + pform_requires_sv(@4, "Task/function default argument"); assert(tmp->size()==1); - tmp->front().defe = $6; + tmp->front().defe = $4; } } /* Rules to match error cases... */ - | port_direction_opt K_var_opt data_type_or_implicit IDENTIFIER error - { yyerror(@3, "error: Error in task/function port item after port name %s.", $4); + | port_direction_opt K_var_opt data_type_or_implicit_plus_id_dim error + { yyerror(@3, "error: Error in task/function port item after port name %s.", $3.id); + delete_type_id_range($3); yyerrok; - $$ = 0; + $$ = nullptr; } ; tf_port_list /* IEEE1800-2005: A.2.7 */ : { port_declaration_context.port_type = gn_system_verilog() ? NetNet::PINPUT : NetNet::PIMPLICIT; - port_declaration_context.data_type = 0; + port_declaration_context.data_type = nullptr; } tf_port_item_list { $$ = $2; } @@ -2816,13 +3049,13 @@ block_item_decl /* variable declarations. Note that data_type can be 0 if we are recovering from an error. */ - : K_const_opt K_var variable_lifetime_opt data_type_or_implicit list_of_variable_decl_assignments ';' - { data_type_t *data_type = $4; + : K_const_opt K_var variable_lifetime_opt list_of_variable_decl_assignments_with_type ';' + { data_type_t*data_type = $4.type; if (!data_type) { - data_type = new vector_type_t(IVL_VT_LOGIC, false, 0); - FILE_NAME(data_type, @2); + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME(data_type, @2); } - pform_make_var(@2, $5, data_type, attributes_in_context, $1); + pform_make_var(@2, $4.decl_assignments, data_type, attributes_in_context, $1); var_lifetime = LexicalScope::INHERITED; } @@ -2831,8 +3064,8 @@ block_item_decl var_lifetime = LexicalScope::INHERITED; } - /* The extra `reg` is not valid (System)Verilog, this is a iverilog extension. */ - | K_const_opt variable_lifetime_opt K_reg data_type list_of_variable_decl_assignments ';' + /* The extra `reg` is not valid (System)Verilog, this is an iverilog extension. */ + | K_const_opt variable_lifetime_opt K_reg reg_prefixed_atomic_type list_of_variable_decl_assignments ';' { if ($4) pform_make_var(@4, $5, $4, attributes_in_context, $1); var_lifetime = LexicalScope::INHERITED; } @@ -2853,11 +3086,6 @@ block_item_decl /* Recover from errors that happen within variable lists. Use the trailing semi-colon to resync the parser. */ - - | K_const_opt K_var variable_lifetime_opt data_type_or_implicit error ';' - { yyerror(@1, "error: Syntax error in variable list."); - yyerrok; - } | K_const_opt variable_lifetime_opt data_type error ';' { yyerror(@1, "error: Syntax error in variable list."); yyerrok; @@ -2945,13 +3173,8 @@ enum_base_type /* IEEE 1800-2012 A.2.2.1 */ : simple_packed_type { $$ = $1; } - | ps_type_identifier dimensions_opt - { if ($2) { - $$ = new parray_type_t($1, $2); - FILE_NAME($$, @1); - } else { - $$ = $1; - } + | ps_type_identifier_dim + { $$ = $1; } | { $$ = new atom_type_t(atom_type_t::INT, true); @@ -4602,16 +4825,16 @@ genvar_identifier_list ; list_of_port_identifiers - : IDENTIFIER dimensions_opt - { $$ = make_port_list($1, @1.lexical_pos, $2, 0); } - | list_of_port_identifiers ',' IDENTIFIER dimensions_opt - { $$ = make_port_list($1, $3, @3.lexical_pos, $4, 0); } + : data_type_or_implicit_plus_id_dim + { $$ = make_port_list($1.type, $1.id, $1.lexical_pos, $1.ranges, nullptr); } + | list_of_port_identifiers ',' identifier_name dimensions_opt + { $$ = make_port_list($1, $3, @3.lexical_pos, $4, nullptr); } ; list_of_variable_port_identifiers - : IDENTIFIER dimensions_opt initializer_opt - { $$ = make_port_list($1, @1.lexical_pos, $2, $3); } - | list_of_variable_port_identifiers ',' IDENTIFIER dimensions_opt initializer_opt + : data_type_or_implicit_plus_id_dim initializer_opt + { $$ = make_port_list($1.type, $1.id, $1.lexical_pos, $1.ranges, $2); } + | list_of_variable_port_identifiers ',' identifier_name dimensions_opt initializer_opt { $$ = make_port_list($1, $3, @3.lexical_pos, $4, $5); } ; @@ -4656,23 +4879,41 @@ list_of_port_declarations tmp->push_back($3); $$ = tmp; } - | list_of_port_declarations ',' attribute_list_opt IDENTIFIER dimensions_opt initializer_opt + | list_of_port_declarations ',' attribute_list_opt partial_port_name_dim initializer_opt { std::vector *ports = $1; Module::port_t* port; if (port_declaration_context.port_type == NetNet::NOT_A_PORT) { yyerror(@4, "error: Incomplete interface port declaration."); - delete[]$4; + delete_type_id_range($4); delete $5; - delete $6; delete $3; port = 0; } else { - port = module_declare_port(@4, $4, + port = module_declare_port(@4, $4.id, $4.lexical_pos, port_declaration_context.port_type, port_declaration_context.port_net_type, port_declaration_context.data_type, - $5, $6, $3); + $4.ranges, $5, $3); + } + ports->push_back(port); + $$ = ports; + } + | list_of_port_declarations ',' attribute_list_opt partial_port_typedef_plus_id_dim initializer_opt + { std::vector *ports = $1; + + Module::port_t* port; + if (port_declaration_context.port_type == NetNet::NOT_A_PORT) { + yyerror(@4, "error: Incomplete interface port declaration."); + delete_type_id_range($4); + delete $5; + delete $3; + port = 0; + } else { + port = module_declare_port(@4, $4.id, $4.lexical_pos, + port_declaration_context.port_type, + NetNet::IMPLICIT, $4.type, + $4.ranges, $5, $3); } ports->push_back(port); $$ = ports; @@ -4686,29 +4927,32 @@ list_of_port_declarations // All of port direction, port kind and data type are optional, but at least // one has to be specified, so we need multiple rules. port_declaration - : attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { $$ = module_declare_port(@5, $5, $2, $3, $4, $6, $7, $1); + : attribute_list_opt port_direction net_type_or_var_opt data_type_or_implicit_plus_id_dim initializer_opt + { $$ = module_declare_port(@4, $4.id, $4.lexical_pos, + $2, $3, $4.type, $4.ranges, $5, $1); } - | attribute_list_opt INTERFACE_IDENTIFIER '.' IDENTIFIER IDENTIFIER dimensions_opt + | attribute_list_opt INTERFACE_IDENTIFIER '.' identifier_name identifier_name dimensions_opt { $$ = module_declare_interface_port(@5, $2, $4, $5, $6, $1); } - | attribute_list_opt INTERFACE_IDENTIFIER IDENTIFIER dimensions_opt + | attribute_list_opt INTERFACE_IDENTIFIER identifier_name dimensions_opt { $$ = module_declare_interface_port(@3, $2, 0, $3, $4, $1); } - | attribute_list_opt net_type_or_var data_type_or_implicit IDENTIFIER dimensions_opt initializer_opt - { pform_requires_sv(@4, "Partial ANSI port declaration"); - $$ = module_declare_port(@4, $4, port_declaration_context.port_type, - $2, $3, $5, $6, $1); - } - | attribute_list_opt data_type_or_implicit_no_opt IDENTIFIER dimensions_opt initializer_opt + | attribute_list_opt net_type_or_var data_type_or_implicit_plus_id_dim initializer_opt { pform_requires_sv(@3, "Partial ANSI port declaration"); - $$ = module_declare_port(@3, $3, port_declaration_context.port_type, - NetNet::IMPLICIT, $2, $4, $5, $1); + $$ = module_declare_port(@3, $3.id, $3.lexical_pos, + port_declaration_context.port_type, + $2, $3.type, $3.ranges, $4, $1); } - | attribute_list_opt port_direction K_wreal IDENTIFIER + | attribute_list_opt partial_port_type_plus_id_dim initializer_opt + { pform_requires_sv(@2, "Partial ANSI port declaration"); + $$ = module_declare_port(@2, $2.id, $2.lexical_pos, + port_declaration_context.port_type, + NetNet::IMPLICIT, $2.type, $2.ranges, $3, $1); + } + | attribute_list_opt port_direction K_wreal identifier_name { real_type_t*real_type = new real_type_t(real_type_t::REAL); FILE_NAME(real_type, @3); - $$ = module_declare_port(@4, $4, $2, NetNet::WIRE, + $$ = module_declare_port(@4, $4, @4.lexical_pos, $2, NetNet::WIRE, real_type, nullptr, nullptr, $1); } ; @@ -5007,7 +5251,19 @@ module_item net_decl_assigns, which are = assignment declarations. */ - | attribute_list_opt net_type drive_strength_opt data_type_or_implicit delay3_opt net_decl_assigns ';' + | attribute_list_opt net_type drive_strength_opt list_of_net_decl_assignments_with_type ';' + { data_type_t*data_type = $4.type; + pform_check_net_data_type(@2, $2, data_type); + check_net_decl_assigns(@4, $4.decl_assignments); + if (!data_type) { + data_type = new vector_type_t(IVL_VT_LOGIC, false, nullptr); + FILE_NAME(data_type, @2); + } + pform_makewire(@2, nullptr, $3, $4.decl_assignments, $2, data_type, $1); + delete $1; + } + + | attribute_list_opt net_type drive_strength_opt data_type_or_implicit delay3 net_decl_assigns ';' { data_type_t*data_type = $4; pform_check_net_data_type(@2, $2, $4); check_net_decl_assigns(@6, $6); @@ -5041,12 +5297,12 @@ module_item input wire signed [h:l] ; This creates the wire and sets the port type all at once. */ - | attribute_list_opt port_direction net_type_or_var data_type_or_implicit list_of_port_identifiers ';' - { pform_module_define_port(@2, $5, $2, $3, $4, $1); } + | attribute_list_opt port_direction net_type_or_var list_of_port_identifiers ';' + { pform_module_define_port(@2, $4.ports, $2, $3, $4.type, $1); } | attribute_list_opt port_direction K_wreal list_of_port_identifiers ';' { real_type_t*real_type = new real_type_t(real_type_t::REAL); - pform_module_define_port(@2, $4, $2, NetNet::WIRE, real_type, $1); + pform_module_define_port(@2, $4.ports, $2, NetNet::WIRE, real_type, $1); } /* The next three rules handle port declarations that include a variable @@ -5055,33 +5311,33 @@ module_item and also handle incomplete port declarations, e.g. input signed [h:l] ; */ - | attribute_list_opt K_inout data_type_or_implicit list_of_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_inout list_of_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::PINOUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::PINOUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::PINOUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::PINOUT, use_type, $3.type, $1); } - | attribute_list_opt K_input data_type_or_implicit list_of_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_input list_of_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::PINPUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::PINPUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::PINPUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::PINPUT, use_type, $3.type, $1); } - | attribute_list_opt K_output data_type_or_implicit list_of_variable_port_identifiers ';' - { NetNet::Type use_type = $3 ? NetNet::IMPLICIT : NetNet::NONE; - if (const vector_type_t*dtype = dynamic_cast ($3)) { + | attribute_list_opt K_output list_of_variable_port_identifiers ';' + { NetNet::Type use_type = $3.type ? NetNet::IMPLICIT : NetNet::NONE; + if (vector_type_t*dtype = dynamic_cast ($3.type)) { if (dtype->implicit_flag) use_type = NetNet::NONE; else @@ -5091,40 +5347,36 @@ module_item // output ports are implicitly (on the inside) // variables because "reg" is not valid syntax // here. - } else if ($3) { + } else if ($3.type) { use_type = NetNet::IMPLICIT_REG; } if (use_type == NetNet::NONE) - pform_set_port_type(@2, $4, NetNet::POUTPUT, $3, $1); + pform_set_port_type(@2, $3.ports, NetNet::POUTPUT, $3.type, $1); else - pform_module_define_port(@2, $4, NetNet::POUTPUT, use_type, $3, $1); + pform_module_define_port(@2, $3.ports, NetNet::POUTPUT, use_type, $3.type, $1); } - | attribute_list_opt port_direction net_type_or_var data_type_or_implicit error ';' + | attribute_list_opt port_direction net_type_or_var error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($4) delete $4; yyerrok; } - | attribute_list_opt K_inout data_type_or_implicit error ';' + | attribute_list_opt K_inout error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } - | attribute_list_opt K_input data_type_or_implicit error ';' + | attribute_list_opt K_input error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } - | attribute_list_opt K_output data_type_or_implicit error ';' + | attribute_list_opt K_output error ';' { yyerror(@2, "error: Invalid variable list in port declaration."); if ($1) delete $1; - if ($3) delete $3; yyerrok; } @@ -5535,19 +5787,22 @@ net_decl_initializer_opt | { $$ = 0; } ; +list_of_net_decl_assignments_with_type /* IEEE1800-2005 A.2.5 */ + : data_type_or_implicit_plus_id_dim net_decl_initializer_opt + { std::list*tmp = new std::list; + tmp->push_back(pform_make_net_decl(@1, $1.id, $1.lexical_pos, $1.ranges, $2)); + $$.decl_assignments = tmp; + $$.type = $1.type; + } + | list_of_net_decl_assignments_with_type ',' net_decl_assign + { $1.decl_assignments->push_back($3); + $$ = $1; + } + ; + net_decl_assign - : IDENTIFIER dimensions_opt net_decl_initializer_opt - { decl_assignment_t*tmp = new decl_assignment_t; - tmp->name = { lex_strings.make($1), @1.lexical_pos }; - if ($2) { - tmp->index = *$2; - if ($3) - pform_requires_sv(@$, "Assignment of net array during declaration"); - delete $2; - } - tmp->expr.reset($3); - delete[]$1; - $$ = tmp; + : identifier_name dimensions_opt net_decl_initializer_opt + { $$ = pform_make_net_decl(@1, $1, $2, $3); } ; @@ -5798,7 +6053,7 @@ port references. The port_t object gets its PWire from the port_reference, but its name from the IDENTIFIER. */ - | '.' IDENTIFIER '(' port_reference ')' + | '.' identifier_name '(' port_reference ')' { Module::port_t*tmp = $4; tmp->name = lex_strings.make($2); delete[]$2; @@ -5818,7 +6073,7 @@ port /* This attaches a name to a port reference concatenation list so that parameter passing be name is possible. */ - | '.' IDENTIFIER '(' '{' port_reference_list '}' ')' + | '.' identifier_name '(' '{' port_reference_list '}' ')' { Module::port_t*tmp = $5; tmp->name = lex_strings.make($2); delete[]$2; @@ -5921,14 +6176,14 @@ port_conn_expression_list_with_nuls port_t object to pass it up to the module declaration code. */ port_reference - : IDENTIFIER + : identifier_name { Module::port_t*ptmp; perm_string name = lex_strings.make($1); ptmp = pform_module_port_reference(@1, name); delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' expression ':' expression ']' + | identifier_name '[' expression ':' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_PART; itmp.msb = $3; @@ -5951,7 +6206,7 @@ port_reference delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' expression ']' + | identifier_name '[' expression ']' { index_component_t itmp; itmp.sel = index_component_t::SEL_BIT; itmp.msb = $3; @@ -5973,7 +6228,7 @@ port_reference delete[]$1; $$ = ptmp; } - | IDENTIFIER '[' error ']' + | identifier_name '[' error ']' { yyerror(@1, "error: Invalid port bit select"); Module::port_t*ptmp = new Module::port_t; PEIdent*wtmp = new PEIdent(lex_strings.make($1), @1.lexical_pos); diff --git a/pform_types.h b/pform_types.h index e16273cf6..021229e53 100644 --- a/pform_types.h +++ b/pform_types.h @@ -510,4 +510,9 @@ extern std::ostream& operator<< (std::ostream&out, const name_component_t&that); extern std::ostream& operator<< (std::ostream&out, const index_component_t&that); extern std::ostream& operator<< (std::ostream&out, const type_restrict_t& type); +struct pform_port_list { + std::list *ports; + data_type_t *type; +}; + #endif /* IVL_pform_types_H */ From 89b2c8bd80cd70c05d6f4b036d95191c86ba4cb0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 25 Jun 2026 23:10:50 -0700 Subject: [PATCH 2/2] Add regression tests for declaration names shadowing type identifiers Check that variable and net declaration names can shadow a visible type identifier. Check this for explicit data type declarations, `var` declarations, and net declarations. Check that task and function formal argument names can shadow a visible type identifier, and that typed arguments still use the visible typedef when an argument name follows. Check ambiguous module port declarations where a type identifier can be either the port name or the port type, with and without dimensions, and that declaration lists continue to use the type selected by the first ambiguous declarator. Cover both ANSI and non-ANSI module port declarations. Signed-off-by: Lars-Peter Clausen --- ivtest/ivltests/sv_type_identifier_net_name.v | 103 ++++++++ .../ivltests/sv_type_identifier_port_name.v | 236 ++++++++++++++++++ ...e_identifier_task_function_argument_name.v | 120 +++++++++ .../sv_type_identifier_variable_name.v | 152 +++++++++++ ivtest/regress-vvp.list | 4 + .../sv_type_identifier_net_name.json | 9 + .../sv_type_identifier_port_name.json | 9 + ...dentifier_task_function_argument_name.json | 9 + .../sv_type_identifier_variable_name.json | 9 + 9 files changed, 651 insertions(+) create mode 100644 ivtest/ivltests/sv_type_identifier_net_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_port_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_task_function_argument_name.v create mode 100644 ivtest/ivltests/sv_type_identifier_variable_name.v create mode 100644 ivtest/vvp_tests/sv_type_identifier_net_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_port_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json create mode 100644 ivtest/vvp_tests/sv_type_identifier_variable_name.json diff --git a/ivtest/ivltests/sv_type_identifier_net_name.v b/ivtest/ivltests/sv_type_identifier_net_name.v new file mode 100644 index 000000000..c92883ea0 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_net_name.v @@ -0,0 +1,103 @@ +// Check that net names can shadow visible type identifiers. + +typedef logic [7:0] T; +typedef int U; +typedef logic [5:0] V; + +`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 net_name(output reg failed); + wire T = 1'b1; + + initial begin + failed = 1'b0; + + `check(T, 1'b1, "wire T did not declare a one-bit net"); + end +endmodule + +module net_list(output reg failed); + wire T, U; + + assign T = 1'b0; + assign U = 1'b1; + + initial begin + failed = 1'b0; + + `check(T, 1'b0, "type identifier net list first value mismatch"); + `check(U, 1'b1, "type identifier net list continuation mismatch"); + end +endmodule + +module net_array(output reg failed); + wire T [1:0]; + + assign T[0] = 1'b0; + assign T[1] = 1'b1; + + initial begin + failed = 1'b0; + + `check(T[0], 1'b0, "type identifier net array first value mismatch"); + `check(T[1], 1'b1, "type identifier net array second value mismatch"); + end +endmodule + +module net_type(output reg failed); + wire T x = 8'ha5; + wire T [1:0] y = 16'h5aa5; + wire V v0, V; + wire T T; + + assign v0 = 6'h2a; + assign V = 6'h15; + assign T = 8'h3c; + + initial begin + failed = 1'b0; + + `check($bits(x), 8, "typed net declaration width regressed"); + `check(x, 8'ha5, "typed net declaration value regressed"); + `check($bits(y), 16, "typed packed net declaration width regressed"); + `check(y, 16'h5aa5, "typed packed net declaration value regressed"); + `check($bits(v0), 6, "type identifier net declaration list first width mismatch"); + `check($bits(V), 6, "type identifier net declaration list did not allow typedef name as continuation"); + `check(V, 6'h15, "type identifier net declaration list value mismatch"); + `check($bits(T), 8, "type-name net declaration did not keep typedef type"); + `check(T, 8'h3c, "type-name net declaration value mismatch"); + end +endmodule + +module test; + reg failed; + wire f0; + wire f1; + wire f2; + wire f3; + + net_name m0(f0); + net_list m1(f1); + net_array m2(f2); + net_type m3(f3); + + initial begin + failed = 1'b0; + + #1; + + `check(f0, 1'b0, "wire T module failed"); + `check(f1, 1'b0, "wire T, U module failed"); + `check(f2, 1'b0, "wire T array module failed"); + `check(f3, 1'b0, "typed wire T module failed"); + + if (!failed) begin + $display("PASSED"); + end + end +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_port_name.v b/ivtest/ivltests/sv_type_identifier_port_name.v new file mode 100644 index 000000000..b5a8f5b41 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_port_name.v @@ -0,0 +1,236 @@ +// Check that module port names can shadow visible type identifiers. + +typedef logic [3:0] T; + +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 ansi_name(input T); +endmodule + +module ansi_type(input T x); +endmodule + +module ansi_name_dim(input T [1:0]); +endmodule + +module ansi_type_dim(input T [1:0] x); +endmodule + +module ansi_name_list(input T, U); +endmodule + +module ansi_type_list(input T x, y); +endmodule + +module ansi_type_list_shadow(input T x, T); +endmodule + +module ansi_type_name(input T T, output logic ok); + initial ok = ($bits(T) == 4); +endmodule + +module ansi_type_redecl(input n, T x); +endmodule + +module ansi_type_redecl_dim(input n, T [1:0] x); +endmodule + +module ansi_name_redecl_dim(input n, T [1:0]); +endmodule + +module ansi_pkg_type_redecl(input n, p::PT x); +endmodule + +module ansi_pkg_type_redecl_dim(input n, p::PT [1:0] x); +endmodule + +module decl_name(T); + input T; +endmodule + +module decl_type(x); + input T x; +endmodule + +module decl_type_dim(x); + input T [1:0] x; +endmodule + +module decl_external(.T(x)); + input x; +endmodule + +module decl_name_list(T, U); + input T, U; +endmodule + +module decl_type_list(x, y); + input T x, y; +endmodule + +module decl_type_list_shadow(x, T); + input T x, T; +endmodule + +module decl_type_name(T, ok); + input T T; + output logic ok; + + initial ok = ($bits(T) == 4); +endmodule + +module test; + + reg failed; + logic n; + T t; + logic n_dim [1:0]; + T [1:0] t_dim; + logic n0, n1; + T t0, t1; + logic rn; + T rt; + T [1:0] rt_dim; + logic rn_dim; + logic rt_name_dim [1:0]; + logic rp_n; + logic [5:0] rp_t; + logic rp_dim_n; + logic [1:0][5:0] rp_t_dim; + logic d_n; + T d_t; + T [1:0] d_t_dim; + logic d_ext; + logic d_n0, d_n1; + T d_t0, d_t1; + T s_t0, s_t1; + T d_s_t0, d_s_t1; + T p_tn; + T d_p_tn; + logic p_tn_ok; + logic d_p_tn_ok; + + ansi_name m0(n); + ansi_type m1(t); + ansi_name_dim m2(n_dim); + ansi_type_dim m3(t_dim); + ansi_name_list m4(n0, n1); + ansi_type_list m5(t0, t1); + ansi_type_list_shadow m17(s_t0, s_t1); + ansi_type_name m19(p_tn, p_tn_ok); + ansi_type_redecl m12(rn, rt); + ansi_type_redecl_dim m13(rn, rt_dim); + ansi_name_redecl_dim m14(rn_dim, rt_name_dim); + ansi_pkg_type_redecl m15(rp_n, rp_t); + ansi_pkg_type_redecl_dim m16(rp_dim_n, rp_t_dim); + decl_name m6(d_n); + decl_type m7(d_t); + decl_type_dim m8(d_t_dim); + decl_external m9(d_ext); + decl_name_list m10(d_n0, d_n1); + decl_type_list m11(d_t0, d_t1); + decl_type_list_shadow m18(d_s_t0, d_s_t1); + decl_type_name m20(d_p_tn, d_p_tn_ok); + + initial begin + failed = 1'b0; + + n = 1'b1; + t = 4'ha; + n_dim[0] = 1'b0; + n_dim[1] = 1'b1; + t_dim[0] = 4'h3; + t_dim[1] = 4'hc; + n0 = 1'b0; + n1 = 1'b1; + t0 = 4'h5; + t1 = 4'ha; + s_t0 = 4'h6; + s_t1 = 4'h9; + p_tn = 4'h3; + rn = 1'b1; + rt = 4'hc; + rt_dim[0] = 4'h3; + rt_dim[1] = 4'ha; + rn_dim = 1'b0; + rt_name_dim[0] = 1'b1; + rt_name_dim[1] = 1'b0; + rp_n = 1'b1; + rp_t = 6'h2a; + rp_dim_n = 1'b0; + rp_t_dim[0] = 6'h15; + rp_t_dim[1] = 6'h2a; + d_n = 1'b1; + d_t = 4'h6; + d_t_dim[0] = 4'h7; + d_t_dim[1] = 4'h8; + d_ext = 1'b0; + d_n0 = 1'b1; + d_n1 = 1'b0; + d_t0 = 4'h9; + d_t1 = 4'h6; + d_s_t0 = 4'h5; + d_s_t1 = 4'ha; + d_p_tn = 4'hc; + + #1; + + `check($bits(n), 1, "input T was not parsed as a port name"); + `check($bits(t), 4, "input T x was not parsed as a typed port"); + `check($bits(n_dim), 2, "input T [1:0] was not parsed as a port array"); + `check($bits(t_dim), 8, "input T [1:0] x was not parsed as a typed port array"); + `check($bits(n0), 1, "input T, U first port did not keep implicit type"); + `check($bits(n1), 1, "input T, U continuation did not keep implicit type"); + `check($bits(t0), 4, "input T x, y first port did not keep typedef type"); + `check($bits(t1), 4, "input T x, y continuation did not keep typedef type"); + `check(t0, 4'h5, "Typed port list first value mismatch"); + `check(t1, 4'ha, "Typed port list continuation mismatch"); + `check($bits(s_t0), 4, "input T x, T first port did not keep typedef type"); + `check($bits(s_t1), 4, "input T x, T continuation did not allow typedef name as port name"); + `check(s_t1, 4'h9, "Typed port list shadowing value mismatch"); + `check($bits(p_tn), 4, "input T T did not keep typedef type"); + `check(p_tn_ok, 1'b1, "input T T was not available as a typed port"); + `check($bits(rn), 1, "input n, T x first port did not keep implicit type"); + `check($bits(rt), 4, "input n, T x second port did not keep typedef type"); + `check(rt, 4'hc, "Inherited-direction typedef port value mismatch"); + `check($bits(rt_dim), 8, "input n, T [1:0] x did not keep typedef packed dimensions"); + `check(rt_dim[0], 4'h3, "Inherited-direction typedef packed array value mismatch"); + `check(rt_dim[1], 4'ha, "Inherited-direction typedef packed array value mismatch"); + `check($bits(rn_dim), 1, "input n, T [1:0] first port did not keep implicit type"); + `check($bits(rt_name_dim), 2, "input n, T [1:0] second port was not parsed as a port array"); + `check($bits(rp_t), 6, "input n, p::PT x second port did not keep package typedef type"); + `check(rp_t, 6'h2a, "Inherited-direction package typedef port value mismatch"); + `check($bits(rp_t_dim), 12, "input n, p::PT [1:0] x did not keep package typedef packed dimensions"); + `check(rp_t_dim[0], 6'h15, "Inherited-direction package typedef packed array value mismatch"); + `check(rp_t_dim[1], 6'h2a, "Inherited-direction package typedef packed array value mismatch"); + `check($bits(d_n), 1, "input T was not parsed as a port declaration name"); + `check($bits(d_t), 4, "input T x was not parsed as a typed port declaration"); + `check($bits(d_t_dim), 8, "input T [1:0] x was not parsed as a typed port declaration array"); + `check($bits(d_ext), 1, ".T(x) was not parsed as an external port name"); + `check($bits(d_n0), 1, "input T, U first declaration did not keep implicit type"); + `check($bits(d_n1), 1, "input T, U continuation declaration did not keep implicit type"); + `check($bits(d_t0), 4, "input T x, y first declaration did not keep typedef type"); + `check($bits(d_t1), 4, "input T x, y continuation declaration did not keep typedef type"); + `check(d_t0, 4'h9, "Typed port declaration list first value mismatch"); + `check(d_t1, 4'h6, "Typed port declaration list continuation mismatch"); + `check($bits(d_s_t0), 4, "input T x, T first declaration did not keep typedef type"); + `check($bits(d_s_t1), 4, "input T x, T declaration did not allow typedef name as port name"); + `check(d_s_t1, 4'ha, "Typed port declaration list shadowing value mismatch"); + `check($bits(d_p_tn), 4, "non-ANSI input T T did not keep typedef type"); + `check(d_p_tn_ok, 1'b1, "non-ANSI input T T was not available as a typed port"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v b/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v new file mode 100644 index 000000000..835c84b82 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_task_function_argument_name.v @@ -0,0 +1,120 @@ +// Check that task and function argument names can shadow visible type identifiers. + +typedef int T; + +module test; + + reg failed; + + `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 + + function int f_name(input T); + return ($bits(T) == 1 && T === 1'b1) ? 32'd33 : -1; + endfunction + + function int f_type(input T value); + return ($bits(value) == 32) ? value : -1; + endfunction + + function int f_type_list(input T value, T); + return $bits(value) + $bits(T); + endfunction + + function int f_type_name(input T T); + return $bits(T); + endfunction + + function int f_decl_name; + input T; + return $bits(T); + endfunction + + function int f_decl_type; + input T value; + return $bits(value); + endfunction + + function int f_decl_type_name; + input T T; + return $bits(T); + endfunction + + task t_name(input T, output int value); + value = ($bits(T) == 1 && T === 1'b1) ? 32'd44 : -1; + endtask + + task t_type(input T value, output int result); + result = ($bits(value) == 32) ? value : -1; + endtask + + task t_type_list(input T value, T, output int result); + result = $bits(value) + $bits(T); + endtask + + task t_type_name(input T T, output int value); + value = $bits(T); + endtask + + task t_decl_name; + input T; + output int value; + value = $bits(T); + endtask + + task t_decl_type; + input T value; + output int result; + result = $bits(value); + endtask + + task t_decl_type_name; + input T T; + output int value; + value = $bits(T); + endtask + + initial begin + int r0; + int r1; + int r2; + int r3; + int r4; + int r5; + int r6; + + failed = 1'b0; + + t_name(1'b1, r0); + t_type(32'd55, r1); + t_type_list(32'd11, 32'd22, r2); + t_decl_name(1'b1, r3); + t_decl_type(32'd33, r4); + t_type_name(32'd44, r5); + t_decl_type_name(32'd55, r6); + + `check(f_name(1'b1), 32'd33, "Function argument did not hide typedef"); + `check(f_type(32'd66), 32'd66, "Function typed argument regressed"); + `check(f_type_list(32'd11, 32'd22), 64, "Function typed argument list shadowing mismatch"); + `check(f_type_name(32'd33), 32, "Function type-name argument did not keep typedef type"); + `check(f_decl_name(1'b1), 1, "Function non-ANSI argument did not hide typedef"); + `check(f_decl_type(32'd66), 32, "Function non-ANSI typed argument regressed"); + `check(f_decl_type_name(32'd77), 32, "Function non-ANSI type-name argument did not keep typedef type"); + `check(r0, 32'd44, "Task argument did not hide typedef"); + `check(r1, 32'd55, "Task typed argument regressed"); + `check(r2, 64, "Task typed argument list shadowing mismatch"); + `check(r3, 1, "Task non-ANSI argument did not hide typedef"); + `check(r4, 32, "Task non-ANSI typed argument regressed"); + `check(r5, 32, "Task type-name argument did not keep typedef type"); + `check(r6, 32, "Task non-ANSI type-name argument did not keep typedef type"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/ivltests/sv_type_identifier_variable_name.v b/ivtest/ivltests/sv_type_identifier_variable_name.v new file mode 100644 index 000000000..c4fadae33 --- /dev/null +++ b/ivtest/ivltests/sv_type_identifier_variable_name.v @@ -0,0 +1,152 @@ +// Check that variable names can shadow visible type identifiers. + +typedef logic [7:0] T; +typedef int V; +typedef int W; +typedef logic [6:0] A; +typedef logic [5:0] B; +typedef logic [4:0] C; +typedef logic [3:0] D; +typedef logic [2:0] E; +typedef logic [1:0] F; +typedef logic [2:0] G; +typedef logic [3:0] H; +typedef logic [4:0] I; +typedef logic [2:0] P; +bit X; + +package p; + var C; + var D x, D; + var P P; +endpackage + +module test; + + reg failed; + + `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 + + typedef logic [3:0] X; + + T outer; + T a, b; + A a0, A; + E E; + int T, U; + var V; + var B b0, B; + var F F; + X x; + + function int f; + int T; + + T = 32'd11; + return T; + endfunction + + function int f_type_name; + H H; + + H = 4'ha; + return $bits(H) + H; + endfunction + + task t(output int value); + int T; + + T = 32'd22; + value = T; + endtask + + task t_type_name(output int value); + I I; + + I = 5'h15; + value = $bits(I) + I; + endtask + + initial begin + int r; + int r_type_name; + int tr_type_name; + var W; + + failed = 1'b0; + + outer = 8'ha5; + a = 8'h33; + b = 8'hcc; + a0 = 7'h2a; + A = 7'h15; + E = 3'h5; + T = 32'd23; + U = 32'd41; + V = 1'b1; + b0 = 6'h2a; + B = 6'h15; + F = 2'h2; + W = 1'b0; + x = 4'hc; + t(r); + t_type_name(tr_type_name); + + begin : block_scope + int T; + + T = 32'd7; + `check(T, 32'd7, "Block declaration did not hide typedef"); + end + + begin : block_type_name + G G; + + G = 3'h3; + `check($bits(G), 3, "Block type-name declaration did not keep typedef type"); + `check(G, 3'h3, "Block type-name declaration value mismatch"); + end + + r_type_name = f_type_name(); + + `check(outer, 8'ha5, "Typedef value changed"); + `check(T, 32'd23, "Module declaration did not hide typedef"); + `check(U, 32'd41, "Declaration list continuation mismatch"); + `check($bits(V), 1, "Module var declaration did not hide typedef width"); + `check(V, 1'b1, "Module var declaration did not hide typedef value"); + `check($bits(W), 1, "Block var declaration did not hide typedef width"); + `check(W, 1'b0, "Block var declaration did not hide typedef value"); + `check(a, 8'h33, "Type declaration list first value mismatch"); + `check(b, 8'hcc, "Type declaration list continuation mismatch"); + `check($bits(a0), 7, "Type declaration list did not keep typedef type"); + `check($bits(A), 7, "Type declaration list did not allow typedef name as continuation"); + `check(A, 7'h15, "Type declaration list shadowing value mismatch"); + `check($bits(E), 3, "Type-name declaration did not keep typedef type"); + `check(E, 3'h5, "Type-name declaration value mismatch"); + `check($bits(b0), 6, "Var declaration list did not keep typedef type"); + `check($bits(B), 6, "Var declaration list did not allow typedef name as continuation"); + `check(B, 6'h15, "Var declaration list shadowing value mismatch"); + `check($bits(F), 2, "Var type-name declaration did not keep typedef type"); + `check(F, 2'h2, "Var type-name declaration value mismatch"); + `check($bits(p::C), 1, "Package var declaration did not hide typedef width"); + `check($bits(p::x), 4, "Package type declaration list first width mismatch"); + `check($bits(p::D), 4, "Package type declaration list did not allow typedef name as continuation"); + `check($bits(p::P), 3, "Package type-name declaration did not keep typedef type"); + `check(f(), 32'd11, "Function declaration did not hide typedef"); + `check(r_type_name, 14, "Function type-name declaration mismatch"); + `check(r, 32'd22, "Task declaration did not hide typedef"); + `check(tr_type_name, 26, "Task type-name declaration mismatch"); + `check($bits(x), 4, "Local typedef did not hide outer identifier"); + `check(x, 4'hc, "Local typedef value mismatch"); + + if (!failed) begin + $display("PASSED"); + end + end + +endmodule diff --git a/ivtest/regress-vvp.list b/ivtest/regress-vvp.list index 424ce1cc6..c25189cda 100644 --- a/ivtest/regress-vvp.list +++ b/ivtest/regress-vvp.list @@ -385,8 +385,12 @@ sv_type_identifier_foreach_name vvp_tests/sv_type_identifier_foreach_name.json sv_type_identifier_genvar_name vvp_tests/sv_type_identifier_genvar_name.json 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_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 +sv_type_identifier_variable_name vvp_tests/sv_type_identifier_variable_name.json sv_type_param_restrict_class1 vvp_tests/sv_type_param_restrict_class1.json sv_type_param_restrict_class2 vvp_tests/sv_type_param_restrict_class2.json sv_type_param_restrict_class_fail1 vvp_tests/sv_type_param_restrict_class_fail1.json diff --git a/ivtest/vvp_tests/sv_type_identifier_net_name.json b/ivtest/vvp_tests/sv_type_identifier_net_name.json new file mode 100644 index 000000000..5b5789167 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_net_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_net_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and SystemVerilog net types are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_port_name.json b/ivtest/vvp_tests/sv_type_identifier_port_name.json new file mode 100644 index 000000000..f94594bb3 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_port_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_port_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and ANSI ports are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json b/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json new file mode 100644 index 000000000..a8ed1e0fd --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_task_function_argument_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_task_function_argument_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and task/function arguments are SystemVerilog", + "type" : "CE" + } +} diff --git a/ivtest/vvp_tests/sv_type_identifier_variable_name.json b/ivtest/vvp_tests/sv_type_identifier_variable_name.json new file mode 100644 index 000000000..fde51c2a7 --- /dev/null +++ b/ivtest/vvp_tests/sv_type_identifier_variable_name.json @@ -0,0 +1,9 @@ +{ + "type" : "normal", + "source" : "sv_type_identifier_variable_name.v", + "iverilog-args" : [ "-g2005-sv" ], + "vlog95" : { + "__comment" : "Typedefs and block/task scope are SystemVerilog", + "type" : "CE" + } +}