From 9d7e8e67ab03199d8815458be5c0e614a4ca7dd9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 15 Aug 2026 21:03:27 -0700 Subject: [PATCH] symbol_search(): Search compilation-unit scope before instance hierarchy `symbol_search()` currently searches the enclosing instance hierarchy before the compilation-unit scope for ordinary names. An enclosing instance scope can therefore hide an earlier compilation-unit variable or object, including when the object is the prefix of a dotted reference. The LRM section 3.12.1 requires a common lookup order. First search the local lexical scopes through the design unit. Next search the compilation-unit scope up to the reference position. If there is no match, resume at the saved instantiation parent and continue through the instance hierarchy. Disable data object lookup when resuming so only enclosing scope names can match. Task and function names follow the same traversal, but the LRM section 23.8.1 lets the terminal subroutine name search the complete compilation unit. Use a separate terminal lexical limit for this. Resolve a prefix such as `object` in `object.func()` at the reference position while allowing a forward declaration of `func`. Signed-off-by: Lars-Peter Clausen --- elab_expr.cc | 9 ++++--- netmisc.h | 17 +++++++------ symbol_search.cc | 64 ++++++++++++++++++++++++++++++++++++------------ 3 files changed, 65 insertions(+), 25 deletions(-) diff --git a/elab_expr.cc b/elab_expr.cc index 561f7a24e..e7a6794b8 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -309,7 +309,8 @@ static const netclass_t* resolve_call_chain_prefix_class(Design*des, NetScope*sc if (link->peek_chain_prefix() == 0) { symbol_search_results sr; - if (!symbol_search(link, des, scope, link->peek_path(), UINT_MAX, &sr)) { + if (!symbol_search(link, des, scope, link->peek_path(), + link->lexical_pos(), &sr, true)) { return 0; } if (!sr.is_scope() || sr.scope->type() != NetScope::FUNC) { @@ -2100,7 +2101,8 @@ unsigned PECallFunction::test_width(Design*des, NetScope*scope, // Search for the symbol. This should turn up a scope. symbol_search_results search_results; - bool search_flag = symbol_search(this, des, scope, path_, UINT_MAX, &search_results); + bool search_flag = symbol_search(this, des, scope, path_, lexical_pos(), + &search_results, true); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::test_width: " @@ -3239,7 +3241,8 @@ NetExpr* PECallFunction::elaborate_expr_(Design*des, NetScope*scope, // Search for the symbol. This should turn up a scope. symbol_search_results search_results; - bool search_flag = symbol_search(this, des, scope, path_, UINT_MAX, &search_results); + bool search_flag = symbol_search(this, des, scope, path_, lexical_pos(), + &search_results, true); if (debug_elaborate) { cerr << get_fileline() << ": PECallFunction::elaborate_expr: " diff --git a/netmisc.h b/netmisc.h index 8ea5f05d9..a9e9c1bb5 100644 --- a/netmisc.h +++ b/netmisc.h @@ -134,14 +134,17 @@ static inline bool test_function_return_value(const symbol_search_results&search return true; } -extern bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, - pform_name_t path, unsigned lexical_pos, - struct symbol_search_results*res, - NetScope*start_scope = nullptr, bool scope_is_bound = false); - extern bool symbol_search(const LineInfo *li, Design *des, NetScope *scope, - const pform_scoped_name_t &path, unsigned lexical_pos, - struct symbol_search_results*res); + pform_name_t path, unsigned int lexical_pos, + struct symbol_search_results *res); + +/* The forward-reference flag applies only to the terminal name. Prefix + components still use lexical_pos. */ +extern bool symbol_search(const LineInfo *li, Design *des, NetScope *scope, + const pform_scoped_name_t &path, + unsigned int lexical_pos, + struct symbol_search_results *res, + bool allow_terminal_forward_reference = false); extern bool check_interface_modport_access(const LineInfo *li, Design *des, const symbol_search_results &res, diff --git a/symbol_search.cc b/symbol_search.cc index afff1bb44..ce4b1d317 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -293,10 +293,11 @@ static bool symbol_search_child_scope( * the initial caller. */ -bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, - pform_name_t path, unsigned lexical_pos, - struct symbol_search_results*res, - NetScope*start_scope, bool scope_is_bound) +static bool symbol_search_(const LineInfo *li, Design *des, NetScope *scope, + pform_name_t path, unsigned int lexical_pos, + unsigned int prefix_lexical_pos, + struct symbol_search_results *res, + NetScope *start_scope, bool scope_is_bound) { assert(scope); @@ -315,21 +316,18 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, name_component_t path_tail = path.back(); path.pop_back(); - // If this is a recursive call, then we need to know that so - // that we can enable the search for scopes. Set the - // recurse_flag to true if this is a recurse. - if (start_scope==0) - start_scope = scope; - NetScope *unit_scope = start_scope->unit(); bool searched_unit_scope = false; + NetScope *instance_parent = nullptr; // If there are components ahead of the tail, symbol_search // recursively. Ideally, the result is a scope that we search // for the tail key, but there are other special cases as well. if (! path.empty()) { - bool flag = symbol_search(li, des, scope, path, lexical_pos, - res, start_scope, scope_is_bound); + bool flag = symbol_search_(li, des, scope, path, + prefix_lexical_pos, + prefix_lexical_pos, res, + start_scope, scope_is_bound); if (! flag) return false; @@ -403,6 +401,9 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, // scope or item. In this case, res->is_found() is false and we may need // to scan upwards to find the scope or item. while (scope) { + if (scope == unit_scope) + searched_unit_scope = true; + if (debug_scopes || debug_elaborate) { cerr << li->get_fileline() << ": symbol_search: " << "Looking for " << path_tail @@ -480,6 +481,21 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, return true; } + // Lookup leaves a design unit through its lexical parent, the + // compilation-unit scope, before continuing up the instance hierarchy + // (LRM 3.12.1). + const bool is_design_unit = + (scope->type() == NetScope::MODULE && !scope->nested_module()) + || scope->type() == NetScope::PACKAGE; + if (!scope_is_bound && !searched_unit_scope && is_design_unit + && unit_scope && scope != unit_scope) { + instance_parent = scope->parent(); + scope = unit_scope; + searched_unit_scope = true; + search_objects = true; + continue; + } + // If there is no prefix, then we are free to scan upwards looking // for a scope name. Note that only scopes can be searched for up // past module boundaries. Stop searching for objects after leaving @@ -489,6 +505,12 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, scope = scope->parent(); + if (scope == nullptr && instance_parent) { + scope = instance_parent; + instance_parent = nullptr; + search_objects = false; + } + // Last chance - try the compilation unit. Note that modules may // reference nets/variables in the compilation unit, even if they // cannot reference variables in containing scope. @@ -529,8 +551,17 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, } bool symbol_search(const LineInfo *li, Design *des, NetScope *scope, - const pform_scoped_name_t &path, unsigned lexical_pos, + pform_name_t path, unsigned int lexical_pos, struct symbol_search_results *res) +{ + return symbol_search_(li, des, scope, path, lexical_pos, lexical_pos, + res, scope, false); +} + +bool symbol_search(const LineInfo *li, Design *des, NetScope *scope, + const pform_scoped_name_t &path, unsigned int lexical_pos, + struct symbol_search_results *res, + bool allow_terminal_forward_reference) { NetScope *search_scope = scope; bool scope_is_bound = false; @@ -542,8 +573,11 @@ bool symbol_search(const LineInfo *li, Design *des, NetScope *scope, scope_is_bound = true; } - return symbol_search(li, des, search_scope, path.name, lexical_pos, - res, search_scope, scope_is_bound); + const unsigned int terminal_lexical_pos = allow_terminal_forward_reference + ? UINT_MAX : lexical_pos; + return symbol_search_(li, des, search_scope, path.name, + terminal_lexical_pos, lexical_pos, res, + search_scope, scope_is_bound); } bool check_interface_modport_access(const LineInfo *li, Design *des,