Honor declaration order for wildcard port connections

IEEE 1800-2023 section 23.3.2.4 defines a `.*` connection as equivalent
to an implicit `.name` connection for every port not connected explicitly.
The existing `.name` path resolves the matching identifier at the
connection's lexical position. Wildcard port matching instead searches at
the end of the scope, making it find declarations after the connection:

    child i_child(.*);
    wire value;

Use the lexical position carried by the wildcard binding when looking up
and creating wildcard connections. The position of `.*` itself matters
because an earlier explicit port connection can create an implicit net
that the wildcard connection should see:

    child i_child(.source(value), .*);

This also preserves the relaxed lookup provided by
`-gno-strict-net-var-declaration`.

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2026-07-26 12:24:21 -07:00
parent 5f479cbad4
commit 8997c01d6f
1 changed files with 7 additions and 4 deletions

View File

@ -1263,19 +1263,22 @@ bool PGModule::match_module_ports_(Design*des, const Module*rmod,
for (unsigned idx = 0 ; idx < npins_ ; idx += 1) {
// Handle wildcard named port.
if (pins_[idx].name[0] == '*') {
const auto &wildcard = pins_[idx];
for (unsigned j = 0 ; j < nexp ; j += 1) {
if (rmod->ports[j] && !pins[j] && !pins_is_explicitly_not_connected[j]) {
pins_fromwc[j] = true;
pform_name_t path_;
path_.push_back(name_component_t(rmod->ports[j]->name));
symbol_search_results sr;
symbol_search(this, des, scope, path_, UINT_MAX, &sr);
symbol_search(&wildcard, des, scope, path_,
wildcard.lexical_pos(), &sr);
if (sr.net != 0 ||
(rmod->ports[j]->is_interface_port() &&
sr.scope != 0 && sr.scope->is_interface())) {
pins[j] = new PEIdent(rmod->ports[j]->name, UINT_MAX, true);
pins[j]->set_lineno(get_lineno());
pins[j]->set_file(get_file());
pins[j] = new PEIdent(
rmod->ports[j]->name,
wildcard.lexical_pos(), true);
pins[j]->set_line(wildcard);
}
}
}