From 9a06cf5483b3c46b9012da72b60fcaa47952f83a Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 2 Aug 2026 11:02:49 -0700 Subject: [PATCH] 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 --- symbol_search.cc | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/symbol_search.cc b/symbol_search.cc index 3baf8d118..a9470562f 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -152,6 +152,12 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, << " in scope " << scope_path(scope) << " 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) 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)) { - 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) { path.push_back(path_tail); 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)) { - 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) { path.push_back(path_tail); res->scope = scope; @@ -242,7 +248,7 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, unsigned int declaration_pos = scope->get_constant_lexical_pos(path_tail.name, is_enum_name); - bool decl_after_use = !scope_is_bound + bool decl_after_use = check_lexical_order && !(declaration_pos <= lexical_pos); if (!gn_strict_parameter_declaration || !decl_after_use) { 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 // been elaborated yet. 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); if (!net) return false;