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:
parent
da1cefe8ea
commit
78382e72d0
|
|
@ -24,6 +24,7 @@
|
||||||
# include "LineInfo.h"
|
# include "LineInfo.h"
|
||||||
# include "StringHeap.h"
|
# include "StringHeap.h"
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
|
# include <vector>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SystemVerilog supports class declarations with their own lexical
|
* 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;
|
bool elaborate(Design*des, NetScope*scope) const;
|
||||||
|
|
||||||
void pform_dump(std::ostream&out) 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 */
|
#endif /* IVL_PPackage_H */
|
||||||
|
|
|
||||||
10
PScope.h
10
PScope.h
|
|
@ -25,6 +25,7 @@
|
||||||
# include "ivl_target.h"
|
# include "ivl_target.h"
|
||||||
# include <map>
|
# include <map>
|
||||||
# include <set>
|
# include <set>
|
||||||
|
# include <unordered_set>
|
||||||
# include <vector>
|
# include <vector>
|
||||||
|
|
||||||
class PEvent;
|
class PEvent;
|
||||||
|
|
@ -67,9 +68,14 @@ class LexicalScope {
|
||||||
// Symbols that are defined or declared in this scope.
|
// Symbols that are defined or declared in this scope.
|
||||||
std::map<perm_string,PNamedItem*>local_symbols;
|
std::map<perm_string,PNamedItem*>local_symbols;
|
||||||
|
|
||||||
// Symbols that are explicitly imported. Bind the imported name
|
// Symbols that are explicitly imported. This contains the package where
|
||||||
// to the package from which the name is imported.
|
// 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;
|
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
|
// Packages that are wildcard imported. When identifiers from
|
||||||
// these packages are referenced, they will be added to the
|
// these packages are referenced, they will be added to the
|
||||||
|
|
|
||||||
25
parse.y
25
parse.y
|
|
@ -2053,6 +2053,30 @@ package_import_item_list
|
||||||
| package_import_item
|
| 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 */
|
package_item /* IEEE1800-2005 A.1.10 */
|
||||||
: timeunits_declaration
|
: timeunits_declaration
|
||||||
| parameter_declaration
|
| parameter_declaration
|
||||||
|
|
@ -2061,6 +2085,7 @@ package_item /* IEEE1800-2005 A.1.10 */
|
||||||
| task_declaration
|
| task_declaration
|
||||||
| data_declaration
|
| data_declaration
|
||||||
| class_declaration
|
| class_declaration
|
||||||
|
| package_export_declaration
|
||||||
;
|
;
|
||||||
|
|
||||||
package_item_list
|
package_item_list
|
||||||
|
|
|
||||||
39
pform.cc
39
pform.cc
|
|
@ -472,41 +472,6 @@ static void add_local_symbol(LexicalScope*scope, perm_string name, PNamedItem*it
|
||||||
scope->local_symbols[name] = item;
|
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)
|
static void check_potential_imports(const struct vlltype&loc, perm_string name, bool tf_call)
|
||||||
{
|
{
|
||||||
LexicalScope*scope = lexical_scope;
|
LexicalScope*scope = lexical_scope;
|
||||||
|
|
@ -515,7 +480,7 @@ static void check_potential_imports(const struct vlltype&loc, perm_string name,
|
||||||
return;
|
return;
|
||||||
if (scope->explicit_imports.find(name) != scope->explicit_imports.end())
|
if (scope->explicit_imports.find(name) != scope->explicit_imports.end())
|
||||||
return;
|
return;
|
||||||
if (find_potential_import(loc, scope, name, tf_call, true))
|
if (pform_find_potential_import(loc, scope, name, tf_call, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
scope = scope->parent_scope();
|
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())
|
if (cur != cur_scope->typedefs.end())
|
||||||
return cur->second;
|
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) {
|
if (pkg) {
|
||||||
cur = pkg->typedefs.find(name);
|
cur = pkg->typedefs.find(name);
|
||||||
if (cur != pkg->typedefs.end())
|
if (cur != pkg->typedefs.end())
|
||||||
|
|
|
||||||
6
pform.h
6
pform.h
|
|
@ -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_end_package_declaration(const struct vlltype&loc);
|
||||||
extern void pform_package_import(const struct vlltype&loc,
|
extern void pform_package_import(const struct vlltype&loc,
|
||||||
PPackage*pkg, const char*ident);
|
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,
|
extern PExpr* pform_package_ident(const struct vlltype&loc,
|
||||||
PPackage*pkg, pform_name_t*ident);
|
PPackage*pkg, pform_name_t*ident);
|
||||||
|
|
|
||||||
108
pform_package.cc
108
pform_package.cc
|
|
@ -72,6 +72,64 @@ void pform_end_package_declaration(const struct vlltype&loc)
|
||||||
pform_pop_scope();
|
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
|
* Do the import early, during processing. This requires that the
|
||||||
* package is declared in pform ahead of time (it is) and that we can
|
* 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);
|
perm_string use_ident = lex_strings.make(ident);
|
||||||
|
|
||||||
// Check that the requested symbol is available.
|
// Check that the requested symbol is available.
|
||||||
map<perm_string,PNamedItem*>::const_iterator cur_sym
|
PPackage *pkg_decl = pform_package_importable(pkg, use_ident);
|
||||||
= pkg->local_symbols.find(use_ident);
|
if (!pkg_decl) {
|
||||||
if (cur_sym == pkg->local_symbols.end()) {
|
|
||||||
cerr << loc.get_fileline() << ": error: "
|
cerr << loc.get_fileline() << ": error: "
|
||||||
"'" << use_ident << "' is not exported by '"
|
"'" << use_ident << "' is not exported by '"
|
||||||
<< pkg->pscope_name() << "'." << endl;
|
<< 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.
|
// 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()) {
|
if (cur_sym != scope->local_symbols.end()) {
|
||||||
cerr << loc.get_fileline() << ": error: "
|
cerr << loc.get_fileline() << ": error: "
|
||||||
"'" << use_ident << "' has already been declared "
|
"'" << 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
|
map<perm_string,PPackage*>::const_iterator cur_pkg
|
||||||
= scope->explicit_imports.find(use_ident);
|
= scope->explicit_imports.find(use_ident);
|
||||||
if (cur_pkg != scope->explicit_imports.end()) {
|
if (cur_pkg != scope->explicit_imports.end()) {
|
||||||
if (cur_pkg->second != pkg) {
|
if (cur_pkg->second != pkg_decl) {
|
||||||
cerr << loc.get_fileline() << ": error: "
|
cerr << loc.get_fileline() << ": error: "
|
||||||
"'" << use_ident << "' has already been "
|
"'" << use_ident << "' has already been "
|
||||||
"imported into this scope from package '"
|
"imported into this scope from package '"
|
||||||
<< cur_pkg->second->pscope_name() << "'." << endl;
|
<< cur_pkg->second->pscope_name() << "'." << endl;
|
||||||
error_count += 1;
|
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 {
|
} else {
|
||||||
list<PPackage*>::const_iterator cur_pkg
|
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,
|
PExpr* pform_package_ident(const struct vlltype&loc,
|
||||||
PPackage*pkg, pform_name_t*ident_name)
|
PPackage*pkg, pform_name_t*ident_name)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue