Skip to content

Verilog: allow interface, package and class names as named formals - #2080

Draft
kroening wants to merge 1 commit into
mainfrom
kroening/frontend-issue4
Draft

Verilog: allow interface, package and class names as named formals#2080
kroening wants to merge 1 commit into
mainfrom
kroening/frontend-issue4

Conversation

@kroening

Copy link
Copy Markdown
Collaborator

Once a name has been declared as an interface, a package or a class, it could no longer be used as the formal name in a named port connection .name(expr) or in a named parameter assignment .NAME(value).

Minimal example

module sub(input logic data_if);
  initial p0: assert(data_if == 1);
endmodule

interface data_if;
  logic value;
  initial value = 1;
endinterface

module main;
  data_if shared();

  sub s(.data_if(shared.value));
endmodule

Before this change, the parser rejected the instantiation with

syntax error, unexpected TOK_INTERFACE_IDENTIFIER, expecting TOK_NON_TYPE_IDENTIFIER or TOK_TYPE_IDENTIFIER before 'data_if'

The same happened for named parameter assignments, e.g. sub #(.data_if(1)) u();.

Why this is legal

The formal name in a named port connection resp. a named parameter assignment is resolved in the port resp. parameter name space of the instantiated module (IEEE 1800-2017, 3.13 and 23.3.2.2), not in the scope in which the instantiation appears. Hence any name may be used there, irrespective of what it denotes locally.

This is aggravated by the fact that interface, package and class names go into the global scope and persist across all input files, so a single interface declaration anywhere in the input would poison the formal-name space of every module instantiation in the design.

The fix

The scanner classifies identifiers into distinct token classes (TOK_NON_TYPE_IDENTIFIER, TOK_TYPE_IDENTIFIER, TOK_INTERFACE_IDENTIFIER, TOK_PACKAGE_IDENTIFIER, TOK_CLASS_IDENTIFIER) from the scope table, but the grammar's any_identifier covered only the first two. The same problem had previously been patched for typedefs only; this change is the general version of that fix:

  • any_identifier now covers all identifier token classes.
  • parameter_identifier, which is used by named parameter assignments, now uses any_identifier.

No action code is needed for the new alternatives, since the scanner gives every identifier token the same irep shape, namely ID_verilog_identifier with ID_base_name set, which is what the consumers read. The grammar remains conflict-free.

port_identifier (the name of a port in a declaration) is deliberately left unchanged. It is a different case, as the name being declared does live in the local scope, and it cannot be widened without introducing grammar conflicts: widening port_identifier gives 2 shift/reduce conflicts for TOK_INTERFACE_IDENTIFIER (states where attribute_instance_brace can be reduced empty ahead of an ANSI port declaration, which itself may start with TOK_INTERFACE_IDENTIFIER), and 6 shift/reduce conflicts each for the other token classes (against the empty reductions of net_type_opt and signing_opt, i.e. an implicitly typed port name is indistinguishable from a data type). Widening individual uses of port_identifier instead produces reduce/reduce conflicts between the port declaration and port reference rules.

Tests

  • regression/verilog/modules/named_port_connection2 -- formals named after a typedef, interface, package and class
  • regression/verilog/modules/named_parameter_assignment1 -- the same for named parameter assignments
  • regression/verilog/interface/port4 -- the interface case above

Once a name had been declared as an interface, package or class, it could
no longer be used as the formal name in a named port connection
`.name(expr)` or in a named parameter assignment `.NAME(value)`. The
parser rejected such code with

  syntax error, unexpected TOK_INTERFACE_IDENTIFIER, expecting
  TOK_NON_TYPE_IDENTIFIER or TOK_TYPE_IDENTIFIER

The formal name in a named port connection or named parameter assignment
is resolved in the port respectively parameter name space of the
instantiated module (IEEE 1800-2017 3.13 and 23.3.2.2), and not in the
scope in which the instantiation appears. Hence any name may be used
there, irrespective of what it denotes locally. Note that interface,
package and class names go into the global scope and persist across all
input files, so a single interface declaration anywhere would poison the
formal-name space of every instantiation in the design.

The scanner classifies identifiers into distinct token classes
(TOK_NON_TYPE_IDENTIFIER, TOK_TYPE_IDENTIFIER, TOK_INTERFACE_IDENTIFIER,
TOK_PACKAGE_IDENTIFIER, TOK_CLASS_IDENTIFIER) using the scope table, but
`any_identifier` covered only the first two. The same problem had
previously been patched for typedefs only. This change extends
`any_identifier` to all identifier token classes, which is the general
version of that fix, and switches `parameter_identifier` (used by named
parameter assignments) to `any_identifier` as well. No action code is
needed for the new alternatives: the scanner gives every identifier token
the same irep shape, namely ID_verilog_identifier with ID_base_name set,
which is what the consumers read. The grammar remains conflict-free.

Note that `port_identifier` (a port name in a declaration) is left
unchanged deliberately: it is a genuinely different case, since the name
being declared does live in the local scope, and widening it is not
possible without introducing grammar conflicts. Widening
`port_identifier` itself yields 2 shift/reduce conflicts for
TOK_INTERFACE_IDENTIFIER (states where `attribute_instance_brace` can be
reduced empty ahead of an ANSI port declaration, which itself may start
with TOK_INTERFACE_IDENTIFIER) and 6 shift/reduce conflicts each for the
other token classes (against the empty reductions of `net_type_opt` and
`signing_opt`, i.e. an implicitly typed port name is indistinguishable
from a data type). Widening individual uses of `port_identifier` instead
produces reduce/reduce conflicts between the port declaration and port
reference rules.

Adds regression tests regression/verilog/modules/named_port_connection2,
regression/verilog/modules/named_parameter_assignment1 and
regression/verilog/interface/port4.
@kroening
kroening marked this pull request as draft August 11, 2026 17:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant