diff --git a/Changes b/Changes index 5166dd8ab..40003440a 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ The contributors that suggested a given feature are shown in []. Thanks! ** Support hierarchical Verilation (#2206). [Yutetsu TAKATSUKASA] +**** Support class extern. + **** Fix false DECLFILENAME on black-boxed modules (#2430). [Philipp Wagner] diff --git a/src/V3Ast.h b/src/V3Ast.h index 6fc2e3c6b..b20e11f14 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2627,7 +2627,8 @@ private: bool m_taskPublic : 1; // Public task bool m_attrIsolateAssign : 1; // User isolate_assignments attribute bool m_classMethod : 1; // Class method - bool m_extern : 1; // Extern prototype + bool m_externProto : 1; // Extern prototype + bool m_externDef : 1; // Extern definition bool m_prototype : 1; // Just a prototype bool m_dpiExport : 1; // DPI exported bool m_dpiImport : 1; // DPI imported @@ -2646,7 +2647,8 @@ public: , m_taskPublic{false} , m_attrIsolateAssign{false} , m_classMethod{false} - , m_extern{false} + , m_externProto{false} + , m_externDef{false} , m_prototype{false} , m_dpiExport{false} , m_dpiImport{false} @@ -2694,8 +2696,10 @@ public: bool attrIsolateAssign() const { return m_attrIsolateAssign; } void classMethod(bool flag) { m_classMethod = flag; } bool classMethod() const { return m_classMethod; } - void isExtern(bool flag) { m_extern = flag; } - bool isExtern() const { return m_extern; } + void isExternProto(bool flag) { m_externProto = flag; } + bool isExternProto() const { return m_externProto; } + void isExternDef(bool flag) { m_externDef = flag; } + bool isExternDef() const { return m_externDef; } void prototype(bool flag) { m_prototype = flag; } bool prototype() const { return m_prototype; } void dpiExport(bool flag) { m_dpiExport = flag; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 966902455..b3b9c2d32 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1081,7 +1081,9 @@ const char* AstClassPackage::broken() const { return nullptr; } void AstClass::insertCache(AstNode* nodep) { - if (VN_IS(nodep, Var) || VN_IS(nodep, NodeFTask) || VN_IS(nodep, EnumItemRef)) { + bool doit = (VN_IS(nodep, Var) || VN_IS(nodep, EnumItemRef) + || (VN_IS(nodep, NodeFTask) && !VN_CAST(nodep, NodeFTask)->isExternProto())); + if (doit) { if (m_members.find(nodep->name()) != m_members.end()) { nodep->v3error("Duplicate declaration of member name: " << nodep->prettyNameQ()); } else { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 607268136..00096213a 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -992,13 +992,36 @@ class LinkDotFindVisitor : public AstNVisitor { // Remember the existing symbol table scope VSymEnt* oldCurSymp = m_curSymp; { + // Change to appropriate package if extern declaration (vs definition) + if (nodep->packagep()) { + AstClassOrPackageRef* cpackagerefp = VN_CAST(nodep->packagep(), ClassOrPackageRef); + if (!cpackagerefp) { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: extern function definition with class-in-class"); + } else { + AstClass* classp = VN_CAST(cpackagerefp->classOrPackagep(), Class); + if (!classp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(classp); + if (!nodep->isExternDef()) { + // Move it to proper spot under the target class + nodep->unlinkFrBack(); + classp->addStmtp(nodep); + nodep->isExternDef(true); // So we check there's a matching extern + nodep->packagep()->unlinkFrBack()->deleteTree(); + } + } + } + } // Create symbol table for the task's vars - m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_packagep); + string name = string{nodep->isExternProto() ? "extern " : ""} + nodep->name(); + m_curSymp = m_statep->insertBlock(m_curSymp, name, nodep, m_packagep); m_curSymp->fallbackp(oldCurSymp); // Convert the func's range to the output variable // This should probably be done in the Parser instead, as then we could // just attach normal signal attributes to it. - if (nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) { + if (!nodep->isExternProto() && nodep->fvarp() && !VN_IS(nodep->fvarp(), Var)) { AstNodeDType* dtypep = VN_CAST(nodep->fvarp(), NodeDType); // If unspecified, function returns one bit; however when we // support NEW() it could also return the class reference. @@ -2634,11 +2657,15 @@ private: if (nodep->classMethod() && nodep->lifetime().isStatic()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'static' class method"); } - if (nodep->isExtern()) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: extern class methods"); + if (nodep->isExternDef()) { + if (!m_curSymp->findIdFallback("extern " + nodep->name())) { + nodep->v3error("extern not found that declares " + nodep->prettyNameQ()); + } } - if (nodep->packagep()) { - nodep->v3warn(E_UNSUPPORTED, "Unsupported: extern function definition"); + if (nodep->isExternProto()) { + if (!m_curSymp->findIdFallback(nodep->name())) { + nodep->v3error("definition not found for extern " + nodep->prettyNameQ()); + } } VSymEnt* oldCurSymp = m_curSymp; { diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index b5ad3db70..b580a213d 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -134,6 +134,13 @@ private: // NodeTask: Remember its name for later resolution // Remember the existing symbol table scope if (m_classp) nodep->classMethod(true); + // V3LinkDot moved the isExternDef into the class, the extern proto was + // checked to exist, and now isn't needed + nodep->isExternDef(false); + if (nodep->isExternProto()) { + VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep); + return; + } VLifetime origLifetime = m_lifetime; { m_lifetime = nodep->lifetime(); diff --git a/src/verilog.y b/src/verilog.y index 92527d41f..375ae85a2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -6028,11 +6028,11 @@ class_method: // ==IEEE: class_method | yPURE yVIRTUAL__ETC memberQualListE method_prototype ';' { $$ = $4; $3.applyToNodes($4); $4->pureVirtual(true); $4->isVirtual(true); } | yEXTERN memberQualListE method_prototype ';' - { $$ = $3; $2.applyToNodes($3); $3->isExtern(true); } + { $$ = $3; $2.applyToNodes($3); $3->isExternProto(true); } // // IEEE: "method_qualifierE class_constructor_declaration" // // part of function_declaration | yEXTERN memberQualListE class_constructor_prototype - { $$ = $3; $2.applyToNodes($3); $3->isExtern(true); } + { $$ = $3; $2.applyToNodes($3); $3->isExternProto(true); } ; // IEEE: class_constructor_prototype diff --git a/test_regress/t/t_class_class.v b/test_regress/t/t_class_class.v index ef8596298..7453ffac1 100644 --- a/test_regress/t/t_class_class.v +++ b/test_regress/t/t_class_class.v @@ -12,6 +12,7 @@ class Cls; class SubCls; int smembera; int smemberb; + // TODO put extern function here or in t_class_extern.v to check link endclass : SubCls SubCls sc; endclass : Cls diff --git a/test_regress/t/t_class_extern.out b/test_regress/t/t_class_extern.out deleted file mode 100644 index 3238d1424..000000000 --- a/test_regress/t/t_class_extern.out +++ /dev/null @@ -1,7 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_extern.v:8:24: Unsupported: extern class methods - 8 | extern function void extfunc(); - | ^~~~~~~ -%Error-UNSUPPORTED: t/t_class_extern.v:11:15: Unsupported: extern function definition - 11 | function void Cls::extfunc(); - | ^~~ -%Error: Exiting due to diff --git a/test_regress/t/t_class_extern.pl b/test_regress/t/t_class_extern.pl index b8b56e6f0..aabcde63e 100755 --- a/test_regress/t/t_class_extern.pl +++ b/test_regress/t/t_class_extern.pl @@ -11,13 +11,11 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(simulator => 1); compile( - fails => $Self->{vlt_all}, - expect_filename => $Self->{golden_filename}, ); -#execute( -# check_finished => 1, -# ); +execute( + check_finished => 1, + ); ok(1); 1; diff --git a/test_regress/t/t_class_extern.v b/test_regress/t/t_class_extern.v index 231031ff1..eb15c625b 100644 --- a/test_regress/t/t_class_extern.v +++ b/test_regress/t/t_class_extern.v @@ -5,17 +5,23 @@ // SPDX-License-Identifier: CC0-1.0 class Cls; - extern function void extfunc(); + extern task extcls(); + extern function int extone(); endclass -function void Cls::extfunc(); +function int Cls::extone(); + return 1; +endfunction + +task Cls::extcls(); $write("*-* All Finished *-*\n"); $finish; -endfunction +endtask module t (/*AUTOARG*/); initial begin - Cls c; - c.extfunc(); + Cls c = new; + if (c.extone() != 1) $stop; + c.extcls(); end endmodule diff --git a/test_regress/t/t_class_extern_bad.out b/test_regress/t/t_class_extern_bad.out new file mode 100644 index 000000000..e324308c0 --- /dev/null +++ b/test_regress/t/t_class_extern_bad.out @@ -0,0 +1,16 @@ +%Error: t/t_class_extern_bad.v:9:16: Duplicate declaration of task: 'nodef' + 9 | extern task nodef(); + | ^~~~~ + t/t_class_extern_bad.v:8:16: ... Location of original declaration + 8 | extern task nodef(); + | ^~~~~ +%Error: t/t_class_extern_bad.v:8:16: definition not found for extern 'nodef' + 8 | extern task nodef(); + | ^~~~~ +%Error: t/t_class_extern_bad.v:9:16: definition not found for extern 'nodef' + 9 | extern task nodef(); + | ^~~~~ +%Error: t/t_class_extern_bad.v:12:6: extern not found that declares 'noproto' + 12 | task Base1::noproto(); + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_extern_bad.pl b/test_regress/t/t_class_extern_bad.pl new file mode 100755 index 000000000..7be596e0f --- /dev/null +++ b/test_regress/t/t_class_extern_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2020 by Wilson Snyder. This program is free software; you +# can redistribute it and/or modify it under the terms of either the GNU +# Lesser General Public License Version 3 or the Perl Artistic License +# Version 2.0. +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +scenarios(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_class_extern_bad.v b/test_regress/t/t_class_extern_bad.v new file mode 100644 index 000000000..86b921820 --- /dev/null +++ b/test_regress/t/t_class_extern_bad.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class Base1; + extern task nodef(); + extern task nodef(); // duplicate +endclass + +task Base1::noproto(); // no such prototype +endtask + +module t (/*AUTOARG*/); +endmodule