Support simple class extends.

This commit is contained in:
Wilson Snyder 2020-08-23 19:37:56 -04:00
parent fefe731105
commit 20206b1e2e
14 changed files with 217 additions and 50 deletions

View File

@ -304,7 +304,7 @@ class AstClass : public AstNodeModule {
// MEMBERS
MemberNameMap m_members; // Members or method children
AstClassPackage* m_packagep = nullptr; // Class package this is under
bool m_virtual; // Virtual class
bool m_virtual = false; // Virtual class
void insertCache(AstNode* nodep);
public:
@ -329,7 +329,7 @@ public:
addStmtp(nodep);
}
AstClassExtends* extendsp() const { return VN_CAST(op4p(), ClassExtends); }
void extendsp(AstNode* nodep) { addNOp2p(nodep); }
void extendsp(AstNode* nodep) { addNOp4p(nodep); }
void clearCache() { m_members.clear(); }
void repairCache();
AstNode* findMember(const string& name) const {
@ -346,13 +346,15 @@ class AstClassExtends : public AstNode {
public:
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp)
: ASTGEN_SUPER(fl) {
setNOp1p(classOrPkgsp); // Only for parser
setNOp2p(classOrPkgsp); // Only for parser
}
ASTNODE_NODE_FUNCS(ClassExtends)
virtual string verilogKwd() const override { return "extends"; }
virtual bool hasDType() const override { return true; }
AstNodeDType* classOrPkgsp() const { return VN_CAST(op1p(), NodeDType); }
void classOrPkgsp(AstNodeDType* nodep) { setOp1p(nodep); }
virtual string verilogKwd() const override { return "extends"; }
AstNodeDType* childDTypep() const { return VN_CAST(op1p(), NodeDType); }
void childDTypep(AstNodeDType* nodep) { setOp1p(nodep); }
AstNode* classOrPkgsp() const { return op2p(); }
void classOrPkgsp(AstNode* nodep) { setOp2p(nodep); }
AstClass* classp() const; // Class being extended (after link)
};
@ -8512,7 +8514,8 @@ private:
string m_name;
string m_cname; // C name, for dpiExports
string m_rtnType; // void, bool, or other return type
string m_argTypes;
string m_argTypes; // Argument types
string m_ctorInits; // Constructor sub-class inits
string m_ifdef; // #ifdef symbol around this function
VBoolOrUnknown m_isConst; // Function is declared const (*this not changed)
VBoolOrUnknown m_isStatic; // Function is declared static (no this)
@ -8574,7 +8577,7 @@ public:
virtual bool same(const AstNode* samep) const override {
const AstCFunc* asamep = static_cast<const AstCFunc*>(samep);
return ((funcType() == asamep->funcType()) && (rtnTypeVoid() == asamep->rtnTypeVoid())
&& (argTypes() == asamep->argTypes())
&& (argTypes() == asamep->argTypes()) && (ctorInits() == asamep->ctorInits())
&& (!(dpiImport() || dpiExport()) || name() == asamep->name()));
}
//
@ -8606,6 +8609,8 @@ public:
void funcPublic(bool flag) { m_funcPublic = flag; }
void argTypes(const string& str) { m_argTypes = str; }
string argTypes() const { return m_argTypes; }
void ctorInits(const string& str) { m_ctorInits = str; }
string ctorInits() const { return m_ctorInits; }
void ifdef(const string& str) { m_ifdef = str; }
string ifdef() const { return m_ifdef; }
void funcType(AstCFuncType flag) { m_funcType = flag; }

View File

@ -264,6 +264,15 @@ public:
puts("static void __Vmtask__final(bool even_cycle, void* symtab);\n");
}
}
void ccallIterateArgs(AstNodeCCall* nodep) {
puts(nodep->argTypes());
bool comma = (nodep->argTypes() != "");
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (comma) puts(", ");
iterate(subnodep);
comma = true;
}
}
// VISITORS
virtual void visit(AstNodeAssign* nodep) override {
@ -373,13 +382,7 @@ public:
}
puts(nodep->funcp()->nameProtect());
puts("(");
puts(nodep->argTypes());
bool comma = (nodep->argTypes() != "");
for (AstNode* subnodep = nodep->argsp(); subnodep; subnodep = subnodep->nextp()) {
if (comma) puts(", ");
iterate(subnodep);
comma = true;
}
ccallIterateArgs(nodep);
if (VN_IS(nodep->backp(), NodeMath) || VN_IS(nodep->backp(), CReturn)) {
// We should have a separate CCall for math and statement usage, but...
puts(")");
@ -1485,6 +1488,13 @@ class EmitCImp : EmitCStmts {
puts(funcNameProtect(nodep, m_modp));
puts("(" + cFuncArgs(nodep) + ")");
if (nodep->isConst().trueKnown()) puts(" const");
// TODO perhaps better to have a new AstCCtorInit so we can pass arguments
// rather than requiring a string here
if (!nodep->ctorInits().empty()) {
puts(": ");
puts(nodep->ctorInits());
}
puts(" {\n");
// "+" in the debug indicates a print from the model
@ -2982,7 +2992,8 @@ void EmitCImp::emitInt(AstNodeModule* modp) {
if (AstClass* classp = VN_CAST(modp, Class)) {
puts("class " + prefixNameProtect(modp));
if (classp->extendsp()) puts(" : public " + classp->extendsp()->classp()->nameProtect());
if (classp->extendsp())
puts(" : public " + prefixNameProtect(classp->extendsp()->classp()));
puts(" {\n");
} else if (optSystemC() && modp->isTop()) {
puts("SC_MODULE(" + prefixNameProtect(modp) + ") {\n");

View File

@ -76,6 +76,10 @@
//######################################################################
// Matcher classes (for suggestion matching)
class LinkNodeMatcherClass : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, Class); }
};
class LinkNodeMatcherFTask : public VNodeMatcher {
public:
virtual bool nodeMatch(const AstNode* nodep) const override { return VN_IS(nodep, NodeFTask); }
@ -2679,13 +2683,6 @@ private:
virtual void visit(AstClass* nodep) override {
UINFO(5, " " << nodep << endl);
checkNoDot(nodep);
for (AstNode* itemp = nodep->membersp(); itemp; itemp = itemp->nextp()) {
if (AstClassExtends* eitemp = VN_CAST(itemp, ClassExtends)) {
// Replace abstract reference with hard pointer
// Will need later resolution when deal with parameters
eitemp->v3warn(E_UNSUPPORTED, "Unsupported: class extends");
}
}
VSymEnt* oldCurSymp = m_curSymp;
VSymEnt* oldModSymp = m_modSymp;
{
@ -2695,6 +2692,39 @@ private:
m_modp = nodep;
iterateChildren(nodep);
}
for (AstNode* itemp = nodep->extendsp(); itemp; itemp = itemp->nextp()) {
if (AstClassExtends* cextp = VN_CAST(itemp, ClassExtends)) {
// Replace abstract reference with hard pointer
// Will need later resolution when deal with parameters
if (cextp->childDTypep() || cextp->dtypep()) continue; // Already converted
AstClassOrPackageRef* cpackagerefp
= VN_CAST(cextp->classOrPkgsp(), ClassOrPackageRef);
if (!cpackagerefp) {
cextp->v3error("Attempting to extend using a non-class ");
} else {
VSymEnt* foundp = m_curSymp->findIdFallback(cpackagerefp->name());
bool ok = false;
if (foundp) {
if (AstClass* classp = VN_CAST(foundp->nodep(), Class)) {
AstClassRefDType* newp
= new AstClassRefDType{nodep->fileline(), classp};
cextp->childDTypep(newp);
VL_DO_DANGLING(cpackagerefp->unlinkFrBack()->deleteTree(),
cpackagerefp);
ok = true;
}
}
if (!ok) {
string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class to extend not found: "
<< cpackagerefp->prettyNameQ() << endl
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
}
}
}
}
// V3Width when determines types needs to find enum values and such
// so add members pointing to appropriate enum values
{

View File

@ -104,11 +104,14 @@ private:
// TYPES
typedef std::map<std::pair<AstScope*, AstVar*>, AstVarScope*> VarToScopeMap;
typedef std::map<AstNodeFTask*, AstClass*> FuncToClassMap;
typedef std::vector<AstInitial*> Initials;
// MEMBERS
VarToScopeMap m_varToScopeMap; // Map for Var -> VarScope mappings
FuncToClassMap m_funcToClassMap; // Map for ctor func -> class
AstAssignW* m_assignwp = nullptr; // Current assignment
AstNodeFTask* m_ctorp = nullptr; // Class constructor
AstClass* m_classp = nullptr; // Current class
V3Graph m_callGraph; // Task call graph
TaskBaseVertex* m_curVxp; // Current vertex we're adding to
Initials m_initialps; // Initial blocks to move
@ -125,6 +128,14 @@ public:
UASSERT_OBJ(iter != m_varToScopeMap.end(), nodep, "No scope for var");
return iter->second;
}
AstClass* getClassp(AstNodeFTask* nodep) {
AstClass* classp = m_funcToClassMap[nodep];
UASSERT_OBJ(classp, nodep, "No class for ctor func");
return classp;
}
void remapFuncClassp(AstNodeFTask* nodep, AstNodeFTask* newp) {
m_funcToClassMap[newp] = getClassp(nodep);
}
bool ftaskNoInline(AstNodeFTask* nodep) { return getFTaskVertex(nodep)->noInline(); }
AstCFunc* ftaskCFuncp(AstNodeFTask* nodep) { return getFTaskVertex(nodep)->cFuncp(); }
void ftaskCFuncp(AstNodeFTask* nodep, AstCFunc* cfuncp) {
@ -202,6 +213,8 @@ private:
if (nodep->isConstructor()) {
m_curVxp->noInline(true);
m_ctorp = nodep;
UASSERT_OBJ(m_classp, nodep, "Ctor not under class");
m_funcToClassMap[nodep] = m_classp;
}
iterateChildren(nodep);
m_curVxp = lastVxp;
@ -229,6 +242,7 @@ private:
// Move initial statements into the constructor
m_initialps.clear();
m_ctorp = nullptr;
m_classp = nodep;
{ // Find m_initialps, m_ctor
iterateChildren(nodep);
}
@ -246,6 +260,7 @@ private:
}
m_initialps.clear();
m_ctorp = nullptr;
m_classp = nullptr;
}
virtual void visit(AstInitial* nodep) override {
m_initialps.push_back(nodep);
@ -312,7 +327,7 @@ class TaskVisitor : public AstNVisitor {
private:
// NODE STATE
// Each module:
// AstNodeFTask::user // True if its been expanded
// AstNodeFTask::user1 // True if its been expanded
// Each funccall
// to TaskRelinkVisitor:
// AstVar::user2p // AstVarScope* to replace varref with
@ -1035,7 +1050,14 @@ private:
cfuncp->dpiImportWrapper(nodep->dpiImport());
cfuncp->isStatic(!(nodep->dpiImport() || nodep->taskPublic() || nodep->classMethod()));
cfuncp->pure(nodep->pure());
cfuncp->isConstructor(nodep->name() == "new");
if (nodep->name() == "new") {
cfuncp->isConstructor(true);
AstClass* classp = m_statep->getClassp(nodep);
if (classp->extendsp()) {
cfuncp->ctorInits(EmitCBaseVisitor::prefixNameProtect(classp->extendsp()->classp())
+ "(vlSymsp)");
}
}
// cfuncp->dpiImport // Not set in the wrapper - the called function has it set
if (cfuncp->dpiExport()) cfuncp->cname(nodep->cname());
@ -1278,6 +1300,8 @@ private:
m_statep->checkPurity(nodep);
}
AstNodeFTask* clonedFuncp = nodep->cloneTree(false);
if (nodep->isConstructor()) m_statep->remapFuncClassp(nodep, clonedFuncp);
AstCFunc* cfuncp = makeUserFunc(clonedFuncp, m_statep->ftaskNoInline(nodep));
if (cfuncp) {
nodep->addNextHere(cfuncp);

View File

@ -2039,10 +2039,9 @@ private:
}
virtual void visit(AstClassExtends* nodep) override {
if (nodep->didWidthAndSet()) return;
nodep->v3warn(E_UNSUPPORTED, "Unsupported: class extends"); // Member/meth access breaks
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
// nodep->dtypep(iterateEditMoveDTypep(nodep)); // data_type '{ pattern }
// userIterateChildren(nodep, nullptr);
if (VN_IS(nodep->childDTypep(), ClassRefDType)) {
nodep->dtypep(iterateEditMoveDTypep(nodep, nodep->childDTypep()));
}
}
virtual void visit(AstMemberDType* nodep) override {
if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed

View File

@ -1,16 +1,4 @@
%Error-UNSUPPORTED: t/t_class_extends.v:13:21: Unsupported: class extends
13 | class Base1 extends Base0;
| ^~~~~
%Error-UNSUPPORTED: t/t_class_extends.v:18:21: Unsupported: class extends
18 | class Base2 extends Base1;
| ^~~~~
%Error-UNSUPPORTED: t/t_class_extends.v:22:19: Unsupported: class extends
22 | class Cls extends Base2;
| ^~~~~
%Error: t/t_class_extends.v:25:4: Can't find typedef: 'T'
25 | T imemberc;
| ^
%Error-UNSUPPORTED: t/t_class_extends.v:33:43: Unsupported: class extends
33 | class uvm__registry #(type T=int) extends uvm_object_wrapper;
| ^~~~~~~~~~~~~~~~~~
%Error: Exiting due to

View File

@ -0,0 +1,21 @@
#!/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(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,45 @@
// 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 Base0;
int baseonly;
int baseover;
function void b_set_bo(int v); baseover = v; endfunction
function int b_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
endclass
class Ext extends Base0;
int baseover;
int extonly;
function void e_set_bo(int v); baseover = v; endfunction
function int e_get_bo(); return baseover; endfunction
function int get_bo(); return baseover; endfunction
endclass
module t (/*AUTOARG*/);
initial begin
Ext c;
c = new;
c.baseonly = 10;
c.baseover = 20;
c.extonly = 30;
if (c.baseonly != 10) $stop;
if (c.baseover != 20) $stop;
if (c.extonly != 30) $stop;
c.b_set_bo(100);
c.e_set_bo(200);
if (c.b_get_bo() != 100) $stop;
if (c.e_get_bo() != 200) $stop;
if (c.get_bo() != 200) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,5 @@
%Error: t/t_class_extends_nf_bad.v:10:19: Class to extend not found: 'IsNotFound'
: ... Suggested alternative: 'IsFound'
10 | class Cls extends IsNotFound;
| ^~~~~~~~~~
%Error: Exiting due to

View File

@ -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;

View File

@ -0,0 +1,14 @@
// 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 IsFound;
endclass
class Cls extends IsNotFound;
endclass
module t (/*AUTOARG*/);
endmodule

View File

@ -4,9 +4,6 @@
%Error: t/t_class_extends_this.v:13:11: Can't find definition of scope/variable: 'this'
13 | if (this.value != 1) $stop;
| ^~~~
%Error-UNSUPPORTED: t/t_class_extends_this.v:17:19: Unsupported: class extends
17 | class Cls extends Base;
| ^~~~
%Error-UNSUPPORTED: t/t_class_extends_this.v:21:11: Unsupported: this
21 | if (this.value != 2) $stop;
| ^~~~

View File

@ -1,4 +1,11 @@
%Error-UNSUPPORTED: t/t_class_member_bad.v:11:20: Unsupported: class extends
11 | class Cls2 extends Base1;
| ^~~~~
%Error: t/t_class_member_bad.v:18:9: Member 'memb3' not found in class 'Cls2'
: ... In instance t
: ... Suggested alternative: 'memb2'
18 | c.memb3 = 3;
| ^~~~~
%Warning-WIDTH: t/t_class_member_bad.v:18:15: Operator ASSIGN expects 1 bits on the Assign RHS, but Assign RHS's CONST '?32?sh3' generates 32 or 2 bits.
: ... In instance t
18 | c.memb3 = 3;
| ^
... Use "/* verilator lint_off WIDTH */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -1,4 +1,6 @@
%Error-UNSUPPORTED: t/t_class_method_bad.v:11:20: Unsupported: class extends
11 | class Cls2 extends Base1;
| ^~~~~
%Error: t/t_class_method_bad.v:18:9: Class method 'meth3' not found in class 'Cls2'
: ... In instance t
: ... Suggested alternative: 'meth2'
18 | c.meth3();
| ^~~~~
%Error: Exiting due to