Merge pull request #863 from larsclausen/export

Add support for package export
This commit is contained in:
Stephen Williams 2023-01-29 10:34:23 -08:00 committed by GitHub
commit 4643f57edb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 448 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

View File

@ -0,0 +1,25 @@
// Check that it is possible to explicitly export an identifier and import it
// from another scope.
package P1;
integer x = 123;
endpackage
package P2;
import P1::x;
export P1::x;
endpackage
module test;
import P2::x;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,25 @@
// Check that it is possible use package wildcard export an identifier and
// import it from another scope.
package P1;
integer x = 123;
endpackage
package P2;
import P1::x;
export P1::*;
endpackage
module test;
import P2::x;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,25 @@
// Check that it is possible use full wildcard export an identifier and import
// it from another scope.
package P1;
integer x = 123;
endpackage
package P2;
import P1::x;
export *::*;
endpackage
module test;
import P2::x;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,25 @@
// Check that it is possible to export an implicitly imported identifier and
// import it again from another scope.
package P1;
integer x = 123;
endpackage
package P2;
import P1::*;
export P1::x;
endpackage
module test;
import P2::x;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,26 @@
// Check that implicitly imported identifiers can be exported through a package
// wildcard export.
package P1;
integer x = 123;
endpackage
package P2;
import P1::*;
export P1::*;
integer y = x; // Creates an import for P1::x
endpackage
module test;
import P2::x;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,28 @@
// Check that it is possible to implicitly import the same identifier through
// multiple paths without causing a conflict.
package P1;
integer x = 123;
endpackage
package P2;
import P1::x;
export P1::x;
endpackage
module test;
// P1::x is visible through either of the imports below. This should not
// create a conflict since it is the same identifier.
import P1::*;
import P2::*;
initial begin
if (x == 123) begin
$display("PASSED");
end else begin
$display("FAILED");
end
end
endmodule

View File

@ -0,0 +1,22 @@
// Check that it is an error to export an identifier from a package from which
// it has not been imported from.
package P1;
integer x;
endpackage
package P2;
import P1::x;
export P1::x;
endpackage
package P3;
import P1::x;
export P2::x; // This should fail, even though P2::x is the same as P1::x
endpackage
module test;
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,17 @@
// Check that it is an error to export an identifier that has not been imported.
package P1;
integer x;
integer y;
endpackage
package P2;
import P1::x;
export P1::y; // Should fail, P1::y has not been imported.
endpackage
module test;
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,23 @@
// Check that an identifier importable through a wildcard import is not exported
// through a wildcard export if the identifier has not been referenced in
// package.
package P1;
integer x = 123;
endpackage
package P2;
import P1::*;
export *::*;
endpackage
module test;
import P2::x; // This should fail, P1::x is not referenced in P2 and hence not
// exportable through P2
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,23 @@
// Check that it is an error to export an identifier that is importable through
// a wildcard import if it creates a conflict with a local identifier.
package P1;
integer x = 123;
endpackage
package P2;
import P1::*;
integer x = 456;
export P1::x; // This should fail, P1::x can not be imported into this scope
// since the name already exists.
endpackage
module test;
import P2::x;
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,25 @@
// Check that it is an error to export an identifier that is importable through
// a wildcard import if it creates a conflict with a local identifier, even if
// the local identifier is declared after the export.
package P1;
integer x = 123;
endpackage
package P2;
import P1::*;
export P1::x; // This should fail, P1::x can not be imported into this scope
// since the there is a local symbol with the same name. Even if
// it is declared after the export.
integer x = 456;
endpackage
module test;
import P2::x;
initial begin
$display("FAILED");
end
endmodule

View File

@ -0,0 +1,22 @@
// Check that an error is reported if trying to export an identifier that is
// declared outside of a package
integer x = 123;
package P1;
endpackage
package P2;
import P1::*;
export P1::x; // This should fail. x is visible in P1, but not declared in P1
endpackage
module test;
import P2::x;
initial begin
$display("FAILED");
end
endmodule

View File

@ -668,6 +668,18 @@ sv_end_labels normal,-g2009 ivltests
sv_end_labels_bad CE,-g2009 ivltests gold=sv_end_labels_bad.gold
sv_end_labels_unnamed CE,-g2009 ivltests gold=sv_end_labels_unnamed.gold
sv_enum1 normal,-g2009 ivltests
sv_export1 normal,-g2009 ivltests
sv_export2 normal,-g2009 ivltests
sv_export3 normal,-g2009 ivltests
sv_export4 normal,-g2009 ivltests
sv_export5 normal,-g2009 ivltests
sv_export6 normal,-g2009 ivltests
sv_export_fail1 CE,-g2009 ivltests
sv_export_fail2 CE,-g2009 ivltests
sv_export_fail3 CE,-g2009 ivltests
sv_export_fail4 CE,-g2009 ivltests
sv_export_fail5 CE,-g2009 ivltests
sv_export_fail6 CE,-g2009 ivltests
sv_for_variable normal,-g2009 ivltests
sv_foreach1 normal,-g2009 ivltests
sv_foreach2 normal,-g2009 ivltests

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)
{