Use `symbol_search()` for task and function calls

Task and function calls used as statements currently use
`Design::find_task()` and `Design::find_function()`, which only search for
scopes of the requested type. A closer variable, named event, or parameter is
skipped. This can call a hidden task or function, and can make the non-void
discarded-return path resolve a different symbol and abort.

Use one `symbol_search()` for both task lookup and the function fallback so
all declarations in the shared name space participate in lookup. Accept a
function scope or the return variable of a recursive function call, while
preserving the existing method fallback.

Allow the terminal task or function name to be declared later while resolving
a receiver prefix at the call position. Use the call position when the method
fallback searches for the receiver as well.

Remove the now unused `Design::find_task()` and `Design::find_function()`
helpers.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-08-11 08:15:24 -07:00
parent e5f2975a97
commit 7c5c1acf02
4 changed files with 36 additions and 56 deletions

View File

@ -251,7 +251,8 @@ class PCallTask : public Statement {
NetProc*elaborate_method_(Design*des, NetScope*scope,
bool add_this_flag = false) const;
NetProc*elaborate_function_(Design*des, NetScope*scope) const;
NetProc *elaborate_function_(
Design *des, NetScope *scope, NetScope *func_scope) const;
NetProc*elaborate_void_function_(Design*des, NetScope*scope,
NetFuncDef*def) const;
NetProc *elaborate_non_void_function_(Design *des, NetScope *scope,

View File

@ -3950,25 +3950,35 @@ NetProc* PCallTask::elaborate_usr(Design*des, NetScope*scope) const
{
ivl_assert(*this, scope);
NetScope*pscope = scope;
if (package_) {
pscope = des->find_package(package_->pscope_name());
ivl_assert(*this, pscope);
symbol_search_results search_results;
pform_scoped_name_t call_path(package_, path_);
NetScope *task = nullptr;
NetScope *func_scope = nullptr;
if (symbol_search(this, des, scope, call_path, lexical_pos(),
&search_results, true)) {
if (search_results.is_scope()) {
if (search_results.scope->type() == NetScope::TASK)
task = search_results.scope;
else if (search_results.scope->type() == NetScope::FUNC)
func_scope = search_results.scope;
} else if (test_function_return_value(search_results)) {
// A recursive function call resolves to the function return
// variable. Its containing scope is the function being called.
func_scope = search_results.scope;
}
}
NetScope*task = des->find_task(pscope, path_);
if (task == 0) {
if (!task) {
// For SystemVerilog this may be a few other things.
if (gn_system_verilog()) {
NetProc *tmp;
// This could be a method attached to a signal
// or defined in this object?
bool try_implicit_this = scope->get_class_scope() && path_.size() == 1;
tmp = elaborate_method_(des, scope, try_implicit_this);
NetProc *tmp = elaborate_method_(des, scope, try_implicit_this);
if (tmp) return tmp;
// Or it could be a function call ignoring the return?
tmp = elaborate_function_(des, scope);
if (tmp) return tmp;
if (func_scope)
return elaborate_function_(des, scope, func_scope);
}
cerr << get_fileline() << ": error: Enable of unknown task "
@ -4307,7 +4317,7 @@ NetProc* PCallTask::elaborate_method_(Design*des, NetScope*scope,
// (internally represented as "@") is handled by there being a
// "this" object in the instance scope.
symbol_search_results sr;
symbol_search(this, des, scope, use_path, UINT_MAX, &sr);
symbol_search(this, des, scope, use_path, lexical_pos(), &sr);
NetNet*net = sr.net;
if (net == 0)
@ -4631,14 +4641,21 @@ NetProc *PCallTask::elaborate_non_void_function_(Design *des, NetScope *scope,
return tmp->elaborate(des, scope);
}
NetProc* PCallTask::elaborate_function_(Design*des, NetScope*scope) const
NetProc *PCallTask::elaborate_function_(
Design *des, NetScope *scope, NetScope *func_scope) const
{
NetFuncDef*func = des->find_function(scope, path_);
ivl_assert(*this, func_scope);
ivl_assert(*this, func_scope->type() == NetScope::FUNC);
// This is not a function, so this task call cannot be a function
// call with a missing return assignment.
if (!func)
return nullptr;
// The function signals might not have been elaborated yet.
if (func_scope->elab_stage() < 2) {
func_scope->need_const_func(true);
const PFunction *pfunc = func_scope->func_pform();
ivl_assert(*this, pfunc);
pfunc->elaborate_sig(des, func_scope);
}
NetFuncDef *func = func_scope->func_def();
ivl_assert(*this, func);
if (gn_system_verilog() && func->is_void())
return elaborate_void_function_(des, scope, func);

View File

@ -1030,38 +1030,6 @@ NetNet* Design::find_signal(NetScope*scope, pform_name_t path)
return 0;
}
NetFuncDef* Design::find_function(NetScope*scope, const pform_name_t&name)
{
assert(scope);
std::list<hname_t> eval_path = eval_scope_path(this, scope, name);
NetScope*func = find_scope(scope, eval_path, NetScope::FUNC);
if (func && (func->type() == NetScope::FUNC)) {
// If a function is used in a parameter definition or in
// a signal declaration, it is possible to get here before
// the function's signals have been elaborated. If this is
// the case, elaborate them now.
if (func->elab_stage() < 2) {
func->need_const_func(true);
const PFunction*pfunc = func->func_pform();
assert(pfunc);
pfunc->elaborate_sig(this, func);
}
return func->func_def();
}
return 0;
}
NetScope* Design::find_task(NetScope*scope, const pform_name_t&name)
{
std::list<hname_t> eval_path = eval_scope_path(this, scope, name);
NetScope*task = find_scope(scope, eval_path, NetScope::TASK);
if (task && (task->type() == NetScope::TASK))
return task;
return 0;
}
void Design::add_node(NetNode*net)
{
assert(net->design_ == 0);

View File

@ -5215,12 +5215,6 @@ class Design {
NetNet*find_signal(NetScope*scope, pform_name_t path);
// Functions
NetFuncDef* find_function(NetScope*scope, const pform_name_t&key);
// Tasks
NetScope* find_task(NetScope*scope, const pform_name_t&name);
// NODES
void add_node(NetNode*);
void del_node(NetNode*);