Support simple class extends.
This commit is contained in:
parent
fefe731105
commit
20206b1e2e
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
|
|
@ -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;
|
||||
| ^~~~
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue