Add support for package export

By default an identifier that has been imported into a package is not
available for imports by other packages. Only imports that have been
exported can be imported again. E.g.

```
package P1;
  int x;
endpackage

package P2;
  import P1::x;
  export P1::x;
endpackage

module test;
  import P2::x; // This will only work if x has been exported.
endmodule
```

Exports follow the same syntax as imports and allow both export of specific
identifiers or wildcard export. Export supports the special `*::*` target,
which will export all imported items.

Add support for handling package exports.

There is one special cases that needs to be considered. Usually when using
wildcard imports from multiple packages it is an error if there multiple
import candidates for an identifier. With exports it is possible that there
are multiple candidates through different packets, but they all refer to
the same identifier. In this case it does not create a conflict. E.g.

```
package P1;
  int x;
endpackage

package P2;
  import P1::x;
  export P1::x;
endpackage

package P3;
   import P1::*;
   import P2::*;
   int y = x; // No import conflict
endpackage
```

Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
This commit is contained in:
Lars-Peter Clausen 2022-10-03 19:05:55 +02:00
parent da1cefe8ea
commit 78382e72d0
6 changed files with 150 additions and 46 deletions

View File

@ -24,6 +24,7 @@
# include "LineInfo.h"
# include "StringHeap.h"
# include <iostream>
# include <vector>
/*
* SystemVerilog supports class declarations with their own lexical
@ -42,6 +43,13 @@ class PPackage : public PScopeExtra, public LineInfo {
bool elaborate(Design*des, NetScope*scope) const;
void pform_dump(std::ostream&out) const;
struct export_t {
PPackage *pkg;
perm_string name;
};
std::vector<export_t> exports;
};
#endif /* IVL_PPackage_H */

View File

@ -25,6 +25,7 @@
# include "ivl_target.h"
# include <map>
# include <set>
# include <unordered_set>
# include <vector>
class PEvent;
@ -67,9 +68,14 @@ class LexicalScope {
// Symbols that are defined or declared in this scope.
std::map<perm_string,PNamedItem*>local_symbols;
// Symbols that are explicitly imported. Bind the imported name
// to the package from which the name is imported.
// 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::map<perm_string,PPackage*>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.
std::map<perm_string,std::unordered_set<PPackage*>> explicit_imports_from;
// Packages that are wildcard imported. When identifiers from
// these packages are referenced, they will be added to the

25
parse.y
View File

@ -2053,6 +2053,30 @@ package_import_item_list
| package_import_item
;
package_export_declaration /* IEEE1800-2017 A.2.1.3 */
: K_export package_export_item_list ';'
| K_export '*' K_SCOPE_RES '*' ';' { pform_package_export(@$, nullptr, nullptr); }
;
package_export_item
: PACKAGE_IDENTIFIER K_SCOPE_RES IDENTIFIER
{ pform_package_export(@2, $1, $3);
delete[] $3;
}
| PACKAGE_IDENTIFIER K_SCOPE_RES TYPE_IDENTIFIER
{ pform_package_export(@2, $1, $3.text);
delete[] $3.text;
}
| PACKAGE_IDENTIFIER K_SCOPE_RES '*'
{ pform_package_export(@2, $1, nullptr);
}
;
package_export_item_list
: package_export_item_list ',' package_export_item
| package_export_item
;
package_item /* IEEE1800-2005 A.1.10 */
: timeunits_declaration
| parameter_declaration
@ -2061,6 +2085,7 @@ package_item /* IEEE1800-2005 A.1.10 */
| task_declaration
| data_declaration
| class_declaration
| package_export_declaration
;
package_item_list

View File

@ -472,41 +472,6 @@ static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*it
scope->local_symbols[name] = item;
}
static PPackage*find_potential_import(const struct vlltype&loc, LexicalScope*scope,
perm_string name, bool tf_call, bool make_explicit)
{
assert(scope);
PPackage*found_pkg = 0;
for (list<PPackage*>::const_iterator cur_pkg = scope->potential_imports.begin();
cur_pkg != scope->potential_imports.end(); ++cur_pkg) {
PPackage*search_pkg = *cur_pkg;
map<perm_string,PNamedItem*>::const_iterator cur_sym
= search_pkg->local_symbols.find(name);
if (cur_sym != search_pkg->local_symbols.end()) {
if (found_pkg && make_explicit) {
cerr << loc.get_fileline() << ": error: "
"Ambiguous use of '" << name << "'. "
"It is exported by both '"
<< found_pkg->pscope_name()
<< "' and by '"
<< search_pkg->pscope_name()
<< "'." << endl;
error_count += 1;
} else {
found_pkg = search_pkg;
if (make_explicit) {
if (tf_call)
scope->possible_imports[name] = found_pkg;
else
scope->explicit_imports[name] = found_pkg;
}
}
}
}
return found_pkg;
}
static void check_potential_imports(const struct vlltype&loc, perm_string name, bool tf_call)
{
LexicalScope*scope = lexical_scope;
@ -515,7 +480,7 @@ static void check_potential_imports(const struct vlltype&loc, perm_string name,
return;
if (scope->explicit_imports.find(name) != scope->explicit_imports.end())
return;
if (find_potential_import(loc, scope, name, tf_call, true))
if (pform_find_potential_import(loc, scope, name, tf_call, true))
return;
scope = scope->parent_scope();
@ -938,7 +903,7 @@ typedef_t* pform_test_type_identifier(const struct vlltype&loc, const char*txt)
if (cur != cur_scope->typedefs.end())
return cur->second;
PPackage*pkg = find_potential_import(loc, cur_scope, name, false, false);
PPackage*pkg = pform_find_potential_import(loc, cur_scope, name, false, false);
if (pkg) {
cur = pkg->typedefs.find(name);
if (cur != pkg->typedefs.end())

View File

@ -203,6 +203,12 @@ extern void pform_start_package_declaration(const struct vlltype&loc,
extern void pform_end_package_declaration(const struct vlltype&loc);
extern void pform_package_import(const struct vlltype&loc,
PPackage*pkg, const char*ident);
extern void pform_package_export(const struct vlltype &loc, PPackage *pkg,
const char *ident);
PPackage *pform_package_importable(PPackage *pkg, perm_string name);
PPackage *pform_find_potential_import(const struct vlltype&loc, LexicalScope*scope,
perm_string name, bool tf_call, bool make_explicit);
extern PExpr* pform_package_ident(const struct vlltype&loc,
PPackage*pkg, pform_name_t*ident);

View File

@ -72,6 +72,64 @@ void pform_end_package_declaration(const struct vlltype&loc)
pform_pop_scope();
}
PPackage *pform_find_potential_import(const struct vlltype&loc, LexicalScope*scope,
perm_string name, bool tf_call, bool make_explicit)
{
assert(scope);
PPackage *found_pkg = nullptr;
for (auto search_pkg : scope->potential_imports) {
PPackage *decl_pkg = pform_package_importable(search_pkg, name);
if (!decl_pkg)
continue;
if (found_pkg && found_pkg != decl_pkg && make_explicit) {
cerr << loc.get_fileline() << ": error: "
"Ambiguous use of '" << name << "'. "
"It is exported by both '"
<< found_pkg->pscope_name()
<< "' and by '"
<< search_pkg->pscope_name()
<< "'." << endl;
error_count++;
continue;
}
found_pkg = decl_pkg;
if (make_explicit) {
if (tf_call)
scope->possible_imports[name] = found_pkg;
else {
scope->explicit_imports[name] = found_pkg;
scope->explicit_imports_from[name].insert(search_pkg);
}
}
}
return found_pkg;
}
PPackage *pform_package_importable(PPackage *pkg, perm_string name)
{
if (pkg->local_symbols.find(name) != pkg->local_symbols.end())
return pkg;
auto import_pkg = pkg->explicit_imports.find(name);
if (import_pkg == pkg->explicit_imports.end())
return nullptr;
for (auto &exp : pkg->exports) {
// *::* 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) &&
(exp.name.nil() || exp.name == name))
return import_pkg->second;
}
return nullptr;
}
/*
* Do the import early, during processing. This requires that the
* package is declared in pform ahead of time (it is) and that we can
@ -85,9 +143,8 @@ void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ide
perm_string use_ident = lex_strings.make(ident);
// Check that the requested symbol is available.
map<perm_string,PNamedItem*>::const_iterator cur_sym
= pkg->local_symbols.find(use_ident);
if (cur_sym == pkg->local_symbols.end()) {
PPackage *pkg_decl = pform_package_importable(pkg, use_ident);
if (!pkg_decl) {
cerr << loc.get_fileline() << ": error: "
"'" << use_ident << "' is not exported by '"
<< pkg->pscope_name() << "'." << endl;
@ -96,7 +153,7 @@ void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ide
}
// Check for conflict with local symbol.
cur_sym = scope->local_symbols.find(use_ident);
auto cur_sym = scope->local_symbols.find(use_ident);
if (cur_sym != scope->local_symbols.end()) {
cerr << loc.get_fileline() << ": error: "
"'" << use_ident << "' has already been declared "
@ -112,17 +169,17 @@ void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ide
map<perm_string,PPackage*>::const_iterator cur_pkg
= scope->explicit_imports.find(use_ident);
if (cur_pkg != scope->explicit_imports.end()) {
if (cur_pkg->second != pkg) {
if (cur_pkg->second != pkg_decl) {
cerr << loc.get_fileline() << ": error: "
"'" << use_ident << "' has already been "
"imported into this scope from package '"
<< cur_pkg->second->pscope_name() << "'." << endl;
error_count += 1;
}
return;
}
scope->explicit_imports[use_ident] = pkg;
scope->explicit_imports[use_ident] = pkg_decl;
scope->explicit_imports_from[use_ident].insert(pkg);
} else {
list<PPackage*>::const_iterator cur_pkg
@ -134,6 +191,43 @@ void pform_package_import(const struct vlltype&loc, PPackage*pkg, const char*ide
}
}
static bool pform_package_exportable(const struct vlltype &loc, PPackage *pkg,
const perm_string &ident)
{
auto import_pkg = pform_cur_package->explicit_imports_from.find(ident);
if (import_pkg != pform_cur_package->explicit_imports_from.end()) {
auto &pkg_list = import_pkg->second;
if (pkg_list.find(pkg) != pkg_list.end())
return true;
}
if (pform_cur_package->local_symbols.find(ident) == pform_cur_package->local_symbols.end()) {
if (pform_find_potential_import(loc, pform_cur_package,
ident, false, true))
return true;
}
cerr << loc.get_fileline() << ": error: "
"`" << ident << "` has not been imported from "
<< pkg->pscope_name() << "." << endl;
error_count++;
return false;
}
void pform_package_export(const struct vlltype &loc, PPackage *pkg, const char *ident)
{
assert(pform_cur_package);
perm_string use_ident;
if (ident) {
use_ident = lex_strings.make(ident);
if (!pform_package_exportable(loc, pkg, use_ident))
return;
}
pform_cur_package->exports.push_back(PPackage::export_t{pkg, use_ident});
}
PExpr* pform_package_ident(const struct vlltype&loc,
PPackage*pkg, pform_name_t*ident_name)
{