Check declaration order for `$unit::` variables, events, and parameters

The LRM section 3.12.1 says that `$unit::` only disambiguates a name in
the compilation-unit scope and does not allow a reference to an item declared
later. Currently every bound scope is exempt from declaration-order checks, so
this incorrectly resolves `value`:

    module test;
      initial $display("%0d", $unit::value);
    endmodule

    integer value;

Apply the existing variable, named event, and parameter declaration-order
checks when lookup is bound to a compilation-unit scope. Other hierarchical,
package, and imported lookups remain bound without comparing unrelated lexical
positions. Task and function lookup remains unchanged, as required by the
exception in the same LRM section.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-08-02 11:02:49 -07:00
parent 8e420f56ff
commit 9a06cf5483
1 changed files with 10 additions and 4 deletions

View File

@ -152,6 +152,12 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
<< " in scope " << scope_path(scope) << " in scope " << scope_path(scope)
<< " scope_is_bound=" << scope_is_bound << endl; << " scope_is_bound=" << scope_is_bound << endl;
} }
// An explicit $unit:: prefix only disambiguates the name and does
// not allow forward references (LRM 3.12.1).
const bool check_lexical_order =
!scope_is_bound || scope->is_unit();
if (scope->genvar_tmp.str() && path_tail.name == scope->genvar_tmp) if (scope->genvar_tmp.str() && path_tail.name == scope->genvar_tmp)
return false; return false;
@ -193,7 +199,7 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
} }
if (NetNet*net = scope->find_signal(path_tail.name)) { if (NetNet*net = scope->find_signal(path_tail.name)) {
bool decl_after_use = !scope_is_bound && !(net->lexical_pos() <= lexical_pos); bool decl_after_use = check_lexical_order && !(net->lexical_pos() <= lexical_pos);
if (!gn_strict_net_var_declaration || !decl_after_use) { if (!gn_strict_net_var_declaration || !decl_after_use) {
path.push_back(path_tail); path.push_back(path_tail);
res->scope = scope; res->scope = scope;
@ -216,7 +222,7 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
} }
if (NetEvent*eve = scope->find_event(path_tail.name)) { if (NetEvent*eve = scope->find_event(path_tail.name)) {
bool decl_after_use = !scope_is_bound && !(eve->lexical_pos() <= lexical_pos); bool decl_after_use = check_lexical_order && !(eve->lexical_pos() <= lexical_pos);
if (!gn_strict_net_var_declaration || !decl_after_use) { if (!gn_strict_net_var_declaration || !decl_after_use) {
path.push_back(path_tail); path.push_back(path_tail);
res->scope = scope; res->scope = scope;
@ -242,7 +248,7 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
unsigned int declaration_pos = unsigned int declaration_pos =
scope->get_constant_lexical_pos(path_tail.name, scope->get_constant_lexical_pos(path_tail.name,
is_enum_name); is_enum_name);
bool decl_after_use = !scope_is_bound bool decl_after_use = check_lexical_order
&& !(declaration_pos <= lexical_pos); && !(declaration_pos <= lexical_pos);
if (!gn_strict_parameter_declaration || !decl_after_use) { if (!gn_strict_parameter_declaration || !decl_after_use) {
path.push_back(path_tail); path.push_back(path_tail);
@ -292,7 +298,7 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
// Finally check the rare case of a signal that hasn't // Finally check the rare case of a signal that hasn't
// been elaborated yet. // been elaborated yet.
if (PWire*wire = scope->find_signal_placeholder(path_tail.name)) { if (PWire*wire = scope->find_signal_placeholder(path_tail.name)) {
if (scope_is_bound || (wire->lexical_pos() <= lexical_pos)) { if (!check_lexical_order || (wire->lexical_pos() <= lexical_pos)) {
NetNet*net = wire->elaborate_sig(des, scope); NetNet*net = wire->elaborate_sig(des, scope);
if (!net) if (!net)
return false; return false;