From 3679d25e4b6fb80c124e93a78bc8654126b0f8fd Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sat, 25 Jul 2026 23:08:54 -0700 Subject: [PATCH] Honor package import lexical order during identifier lookup Package imports become visible from the import declaration, or for wildcard imports, from the reference that makes an identifier locally visible. For example: package p; parameter X = 2; endpackage parameter X = 1; module m; localparam A = X; import p::X; localparam B = X; endmodule Currently both `A` and `B` resolve to `p::X`. `A` should resolve to the compilation-unit `X`, while only `B` should resolve to `p::X`. Record the lexical position where each imported name becomes locally visible. Pass reference positions through ordinary symbol lookup and implicit named port connections so imports introduced later are ignored. Signed-off-by: Lars-Peter Clausen --- PScope.h | 14 ++++++++++++-- net_scope.cc | 23 +++++++++++++---------- netlist.h | 8 +++++--- pform.cc | 17 +++++++---------- pform_package.cc | 33 ++++++++++++++++++++++----------- symbol_search.cc | 3 ++- 6 files changed, 61 insertions(+), 37 deletions(-) diff --git a/PScope.h b/PScope.h index b4f1b2029..c92fa620c 100644 --- a/PScope.h +++ b/PScope.h @@ -43,6 +43,16 @@ class PCallTask; class Design; class NetScope; +struct package_import_t : public LineInfo { + package_import_t() = default; + explicit package_import_t(PPackage *pkg) : package(pkg) { } + + PPackage *package = nullptr; + // LineInfo records where the imported name became locally visible. +}; + +using package_import_map_t = std::map; + /* * The PScope class is a base representation of an object that * represents lexical scope. For example, a module, a function/task, a @@ -71,7 +81,7 @@ class LexicalScope { // Symbols that are explicitly imported. This contains the package where // the symbol has been decelared. When using exports, this might not be // the same as the package where it has been imported from. - std::mapexplicit_imports; + package_import_map_t explicit_imports; // Symbols that are explicitly imported. This contains the set of // packages from which the symbol has been imported. When using exports // the same identifier can be imported via multiple packages. @@ -86,7 +96,7 @@ class LexicalScope { // later in the scope. So here we stash the potential imports for // task and function calls. They will be added to the explicit // imports if we don't find a local definition. - std::mappossible_imports; + package_import_map_t possible_imports; struct range_t { // True if this is an exclude diff --git a/net_scope.cc b/net_scope.cc index 13a31ed80..ef8ee794a 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -111,7 +111,6 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t, NetScope*in_u : type_(t), name_(n), nested_module_(nest), program_block_(program), is_interface_(interface), is_unit_(compilation_unit), unit_(in_unit), up_(up) { - imports_ = 0; events_ = 0; lcounter_ = 0; is_auto_ = false; @@ -215,22 +214,26 @@ void NetScope::set_line(perm_string file, perm_string def_file, def_lineno_ = def_lineno; } -void NetScope::add_imports(const map*imports) +void NetScope::add_imports(const package_import_map_t *imports) { if (!imports->empty()) imports_ = imports; } -NetScope*NetScope::find_import(const Design*des, perm_string name) +NetScope *NetScope::find_import(const Design *des, perm_string name, + unsigned int lexical_pos) { - if (imports_ == 0) - return 0; + if (!imports_) + return nullptr; - map::const_iterator cur = imports_->find(name); - if (cur != imports_->end()) { - return des->find_package(cur->second->pscope_name()); - } else - return 0; + auto cur = imports_->find(name); + if (cur == imports_->end()) + return nullptr; + + if (cur->second.lexical_pos() > lexical_pos) + return nullptr; + + return des->find_package(cur->second.package->pscope_name()); } void NetScope::add_typedefs(const map*typedefs) diff --git a/netlist.h b/netlist.h index 8643158c2..ba1d3936a 100644 --- a/netlist.h +++ b/netlist.h @@ -26,6 +26,7 @@ * can be passed around in this form to the various stages and design * processors. */ +# include # include # include # include @@ -997,8 +998,9 @@ class NetScope : public Definitions, public Attrib { if a unique name couldn't be generated. */ bool auto_name(const char* prefix, char pad, const char* suffix); - void add_imports(const std::map*imports); - NetScope*find_import(const Design*des, perm_string name); + void add_imports(const package_import_map_t *imports); + NetScope *find_import(const Design *des, perm_string name, + unsigned int lexical_pos = UINT_MAX); void add_typedefs(const std::map*typedefs); @@ -1377,7 +1379,7 @@ class NetScope : public Definitions, public Attrib { signed char time_unit_, time_prec_; bool time_from_timescale_; - const std::map*imports_; + const package_import_map_t *imports_ = nullptr; std::maptypedefs_; diff --git a/pform.cc b/pform.cc index a6927eaff..c33c49dae 100644 --- a/pform.cc +++ b/pform.cc @@ -412,10 +412,9 @@ LexicalScope* pform_peek_scope(void) static void pform_check_possible_imports(LexicalScope *scope) { - map::const_iterator cur; - for (cur = scope->possible_imports.begin(); cur != scope->possible_imports.end(); ++cur) { - if (scope->local_symbols.find(cur->first) == scope->local_symbols.end()) - scope->explicit_imports[cur->first] = cur->second; + for (const auto &cur : scope->possible_imports) { + if (!scope->local_symbols.count(cur.first)) + scope->explicit_imports[cur.first] = cur.second; } scope->possible_imports.clear(); } @@ -473,13 +472,12 @@ static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*it } // Check for conflict with an explicit import. - map::const_iterator cur_pkg - = scope->explicit_imports.find(name); + auto cur_pkg = scope->explicit_imports.find(name); if (cur_pkg != scope->explicit_imports.end()) { cerr << item->get_fileline() << ": error: " "'" << name << "' has already been " "imported into this scope from package '" - << cur_pkg->second->pscope_name() << "'." << endl; + << cur_pkg->second.package->pscope_name() << "'." << endl; error_count += 1; return; } @@ -931,10 +929,9 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt) // something other than a type, then give up now because // the name has at least shadowed any other possible // meaning for this name. - map::iterator cur_pkg; - cur_pkg = cur_scope->explicit_imports.find(name); + auto cur_pkg = cur_scope->explicit_imports.find(name); if (cur_pkg != cur_scope->explicit_imports.end()) { - PPackage*pkg = cur_pkg->second; + auto pkg = cur_pkg->second.package; cur = pkg->typedefs.find(name); if (cur != pkg->typedefs.end()) return cur->second; diff --git a/pform_package.cc b/pform_package.cc index a1ab9c060..c9b9660dc 100644 --- a/pform_package.cc +++ b/pform_package.cc @@ -41,6 +41,14 @@ static map packages_by_name; static PPackage*pform_cur_package = 0; +static package_import_t make_package_import(PPackage *pkg, + const struct vlltype &loc) +{ + package_import_t import(pkg); + FILE_NAME(&import, loc); + return import; +} + void pform_start_package_declaration(const struct vlltype&loc, const char*name, LexicalScope::lifetime_t lifetime) { @@ -97,10 +105,12 @@ PPackage *pform_find_potential_import(const struct vlltype&loc, LexicalScope*sco found_pkg = decl_pkg; if (make_explicit) { - if (tf_call) - scope->possible_imports[name] = found_pkg; - else { - scope->explicit_imports[name] = found_pkg; + if (tf_call) { + scope->possible_imports.emplace( + name, make_package_import(found_pkg, loc)); + } else { + scope->explicit_imports.emplace( + name, make_package_import(found_pkg, loc)); scope->explicit_imports_from[name].insert(search_pkg); } } @@ -122,9 +132,9 @@ PPackage *pform_package_importable(PPackage *pkg, perm_string name) // *::* will match all imports, P::* will match all imports // from a package and P::ID will match a specific identifier // from a package. - if ((!exp.pkg || exp.pkg == import_pkg->second) && + if ((!exp.pkg || exp.pkg == import_pkg->second.package) && (exp.name.nil() || exp.name == name)) - return import_pkg->second; + return import_pkg->second.package; } return nullptr; @@ -166,19 +176,20 @@ void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ide } // Check for conflict with previous import. - map::const_iterator cur_pkg - = scope->explicit_imports.find(use_ident); + auto cur_pkg = scope->explicit_imports.find(use_ident); if (cur_pkg != scope->explicit_imports.end()) { - if (cur_pkg->second != pkg_decl) { + if (cur_pkg->second.package != pkg_decl) { cerr << loc.get_fileline() << ": error: " "'" << use_ident << "' has already been " "imported into this scope from package '" - << cur_pkg->second->pscope_name() << "'." << endl; + << cur_pkg->second.package->pscope_name() + << "'." << endl; error_count += 1; } } - scope->explicit_imports[use_ident] = pkg_decl; + scope->explicit_imports.emplace( + use_ident, make_package_import(pkg_decl, loc)); scope->explicit_imports_from[use_ident].insert(pkg); } else { diff --git a/symbol_search.cc b/symbol_search.cc index 863c9717e..c5a29aa47 100644 --- a/symbol_search.cc +++ b/symbol_search.cc @@ -372,7 +372,8 @@ bool symbol_search(const LineInfo*li, Design*des, NetScope*scope, break; // Imports are not visible through hierachical names - if (NetScope*import_scope = scope->find_import(des, path_tail.name)) { + if (auto import_scope = scope->find_import( + des, path_tail.name, lexical_pos)) { scope = import_scope; continue; }