symbol_search(): Factor object lookup into a helper

The object lookup for each scope is embedded in the scope traversal. This
makes the traversal order and the module boundary handling difficult to
follow.

Move net, event, parameter, class property, and placeholder lookup into a
helper. Use separate found, not found, and failure results so the existing
lookup behavior is preserved. This prepares the scope traversal to be
changed independently.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-08-15 20:58:10 -07:00
parent 1d2aa1b6fa
commit cf6f70e03b
1 changed files with 174 additions and 135 deletions

View File

@ -30,6 +30,173 @@
using namespace std;
enum class scope_object_search_result_t {
not_found,
found,
failed
};
static scope_object_search_result_t symbol_search_scope_objects(
const LineInfo *li, Design *des, NetScope *scope,
NetScope *start_scope, const pform_name_t &path,
const name_component_t &path_tail, unsigned int lexical_pos,
bool check_lexical_order, struct symbol_search_results *res)
{
// Special case `super` keyword. Return the `this` object, but
// with the type of the base class.
if (path_tail.name == "#") {
if (NetNet *net = scope->find_signal(
perm_string::literal(THIS_TOKEN))) {
const netclass_t *class_type =
dynamic_cast<const netclass_t*>(net->net_type());
ivl_assert(*li, class_type);
if (!class_type->get_super()) {
cerr << li->get_fileline() << ": error: "
<< "Class " << class_type->get_name()
<< " uses `super` without a base class."
<< endl;
des->errors += 1;
return scope_object_search_result_t::failed;
}
pform_name_t path_head = path;
path_head.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = class_type->get_super();
res->path_head = path_head;
return scope_object_search_result_t::found;
}
return scope_object_search_result_t::failed;
}
if (NetNet *net = scope->find_signal(path_tail.name)) {
bool decl_after_use = check_lexical_order
&& !(net->lexical_pos() <= lexical_pos);
if (!gn_strict_net_var_declaration || !decl_after_use) {
pform_name_t path_head = path;
path_head.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = net->net_type();
res->path_head = path_head;
if (warn_decl_after_use && decl_after_use) {
cerr << li->get_fileline()
<< ": warning: net/variable `" << path_tail.name
<< "` used before declaration." << endl;
cerr << net->get_fileline()
<< ": : the net/variable is declared here."
<< endl;
// suppress further warnings for this net
net->lexical_pos(lexical_pos);
}
return scope_object_search_result_t::found;
} else if (!res->decl_after_use) {
res->decl_after_use = net;
}
}
if (NetEvent *eve = scope->find_event(path_tail.name)) {
bool decl_after_use = check_lexical_order
&& !(eve->lexical_pos() <= lexical_pos);
if (!gn_strict_net_var_declaration || !decl_after_use) {
pform_name_t path_head = path;
path_head.push_back(path_tail);
res->scope = scope;
res->eve = eve;
res->path_head = path_head;
if (warn_decl_after_use && decl_after_use) {
cerr << li->get_fileline()
<< ": warning: event `" << path_tail.name
<< "` used before declaration." << endl;
cerr << eve->get_fileline()
<< ": : the event is declared here." << endl;
// suppress further warnings for this event
eve->lexical_pos(lexical_pos);
}
return scope_object_search_result_t::found;
} else if (!res->decl_after_use) {
res->decl_after_use = eve;
}
}
if (const NetExpr *par = scope->get_parameter(
des, path_tail.name, res->type)) {
bool is_enum_name;
unsigned int declaration_pos =
scope->get_constant_lexical_pos(path_tail.name,
is_enum_name);
bool decl_after_use = check_lexical_order
&& !(declaration_pos <= lexical_pos);
if (!gn_strict_parameter_declaration || !decl_after_use) {
pform_name_t path_head = path;
path_head.push_back(path_tail);
res->scope = scope;
res->par_val = par;
res->path_head = path_head;
if (warn_decl_after_use && decl_after_use) {
const char *kind = is_enum_name
? "enum named constant" : "parameter";
cerr << li->get_fileline()
<< ": warning: " << kind << " `"
<< path_tail.name
<< "` used before declaration." << endl;
cerr << par->get_fileline()
<< ": : the " << kind
<< " is declared here." << endl;
// suppress further warnings for this constant
scope->set_constant_lexical_pos(
path_tail.name, lexical_pos);
}
return scope_object_search_result_t::found;
} else if (!res->decl_after_use) {
res->decl_after_use = par;
}
}
// Static items are just normal signals and are found above.
if (scope->type() == NetScope::CLASS) {
const netclass_t *clsnet = scope->class_def();
int pidx = clsnet->property_idx_from_name(path_tail.name);
if (pidx >= 0) {
// This is a class property being accessed in a
// class method. Return `this` for the net and the
// property name for the path tail.
NetScope *scope_method =
find_method_containing_scope(*li, start_scope);
ivl_assert(*li, scope_method);
res->net = scope_method->find_signal(
perm_string::literal(THIS_TOKEN));
ivl_assert(*li, res->net);
res->scope = scope;
ivl_assert(*li, path.empty());
res->path_head.push_back(name_component_t(
perm_string::literal(THIS_TOKEN)));
res->path_tail.push_front(path_tail);
res->type = clsnet;
return scope_object_search_result_t::found;
}
}
// 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 (!check_lexical_order || (wire->lexical_pos() <= lexical_pos)) {
NetNet *net = wire->elaborate_sig(des, scope);
if (!net)
return scope_object_search_result_t::failed;
pform_name_t path_head = path;
path_head.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = net->net_type();
res->path_head = path_head;
return scope_object_search_result_t::found;
}
}
return scope_object_search_result_t::not_found;
}
/*
* Search for the hierarchical name. The path may have multiple components. If
* that's the case, then recursively pull the path apart until we find the
@ -174,142 +341,14 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope,
// ... top.not_ok; // Matches.
// endmodule
if (!passed_module_boundary) {
// Special case `super` keyword. Return the `this` object, but
// with the type of the base class.
if (path_tail.name == "#") {
if (NetNet *net = scope->find_signal(perm_string::literal(THIS_TOKEN))) {
const netclass_t *class_type = dynamic_cast<const netclass_t*>(net->net_type());
ivl_assert(*li, class_type);
if (!class_type->get_super()) {
cerr << li->get_fileline() << ": error: "
<< "Class " << class_type->get_name()
<< " uses `super` without a base class."
<< endl;
des->errors += 1;
return false;
}
path.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = class_type->get_super();
res->path_head = path;
return true;
}
scope_object_search_result_t object_result =
symbol_search_scope_objects(
li, des, scope, start_scope, path, path_tail,
lexical_pos, check_lexical_order, res);
if (object_result == scope_object_search_result_t::found)
return true;
if (object_result == scope_object_search_result_t::failed)
return false;
}
if (NetNet*net = scope->find_signal(path_tail.name)) {
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;
res->net = net;
res->type = net->net_type();
res->path_head = path;
if (warn_decl_after_use && decl_after_use) {
cerr << li->get_fileline()
<< ": warning: net/variable `" << path_tail.name
<< "` used before declaration." << endl;
cerr << net->get_fileline()
<< ": : the net/variable is declared here." << endl;
// suppress further warnings for this net
net->lexical_pos(lexical_pos);
}
return true;
} else if (!res->decl_after_use) {
res->decl_after_use = net;
}
}
if (NetEvent*eve = scope->find_event(path_tail.name)) {
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;
res->eve = eve;
res->path_head = path;
if (warn_decl_after_use && decl_after_use) {
cerr << li->get_fileline()
<< ": warning: event `" << path_tail.name
<< "` used before declaration." << endl;
cerr << eve->get_fileline()
<< ": : the event is declared here." << endl;
// suppress further warnings for this event
eve->lexical_pos(lexical_pos);
}
return true;
} else if (!res->decl_after_use) {
res->decl_after_use = eve;
}
}
if (const NetExpr *par = scope->get_parameter(des, path_tail.name, res->type)) {
bool is_enum_name;
unsigned int declaration_pos =
scope->get_constant_lexical_pos(path_tail.name,
is_enum_name);
bool decl_after_use = check_lexical_order
&& !(declaration_pos <= lexical_pos);
if (!gn_strict_parameter_declaration || !decl_after_use) {
path.push_back(path_tail);
res->scope = scope;
res->par_val = par;
res->path_head = path;
if (warn_decl_after_use && decl_after_use) {
const char *kind = is_enum_name
? "enum named constant" : "parameter";
cerr << li->get_fileline()
<< ": warning: " << kind << " `"
<< path_tail.name
<< "` used before declaration." << endl;
cerr << par->get_fileline()
<< ": : the " << kind
<< " is declared here." << endl;
// suppress further warnings for this constant
scope->set_constant_lexical_pos(path_tail.name, lexical_pos);
}
return true;
} else if (!res->decl_after_use) {
res->decl_after_use = par;
}
}
// Static items are just normal signals and are found above.
if (scope->type() == NetScope::CLASS) {
const netclass_t *clsnet = scope->class_def();
int pidx = clsnet->property_idx_from_name(path_tail.name);
if (pidx >= 0) {
// This is a class property being accessed in a
// class method. Return `this` for the net and the
// property name for the path tail.
NetScope *scope_method = find_method_containing_scope(*li, start_scope);
ivl_assert(*li, scope_method);
res->net = scope_method->find_signal(perm_string::literal(THIS_TOKEN));
ivl_assert(*li, res->net);
res->scope = scope;
ivl_assert(*li, path.empty());
res->path_head.push_back(name_component_t(perm_string::literal(THIS_TOKEN)));
res->path_tail.push_front(path_tail);
res->type = clsnet;
return true;
}
}
// 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 (!check_lexical_order || (wire->lexical_pos() <= lexical_pos)) {
NetNet*net = wire->elaborate_sig(des, scope);
if (!net)
return false;
path.push_back(path_tail);
res->scope = scope;
res->net = net;
res->type = net->net_type();
res->path_head = path;
return true;
}
}
}
// Could not find an object. Maybe this is a child scope name? If