Support class extends with arguments.

This commit is contained in:
Wilson Snyder 2025-04-08 22:09:40 -04:00
parent 86f6ac2960
commit 7c2b1971a4
15 changed files with 175 additions and 42 deletions

View File

@ -24,6 +24,7 @@ Verilator 5.035 devel
* Support command-line overriding `define (#5900) (#5908). [Brian Li]
* Support `$setuphold` (#5884). [Krzysztof Sychla]
* Support `systemc_interface and related inside `class`.
* Support class extends with arguments.
* Change `--trace` to `--trace-vcd`.
* Add multi-thread hierarchical simulation (#2583) (#5871). [Bartłomiej Chmiel, Antmicro Ltd.]
* Add check for `let` misused in statement context (#5733).

View File

@ -4407,12 +4407,15 @@ public:
class AstNew final : public AstNodeFTaskRef {
// New as constructor
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
bool m_isImplicit = false; // Implicitly generated from extends args
public:
AstNew(FileLine* fl, AstNodeExpr* pinsp)
: ASTGEN_SUPER_New(fl, "new", pinsp) {}
ASTGEN_MEMBERS_AstNew;
bool sameNode(const AstNode* /*samep*/) const override { return true; }
int instrCount() const override { return widthInstrs(); }
bool isImplicit() const { return m_isImplicit; }
void isImplicit(bool flag) { m_isImplicit = flag; }
};
class AstTaskRef final : public AstNodeFTaskRef {
// A reference to a task

View File

@ -915,6 +915,7 @@ class AstClassExtends final : public AstNode {
// during early parse, then moves to dtype
// @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op2 := classOrPkgsp : Optional[AstNode]
// @astgen op3 := argsp : List[AstNodeExpr]
const bool m_isImplements = false; // class implements
bool m_parameterized = false; // has parameters in its statement

View File

@ -2254,7 +2254,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
// (except the default instances)
// They are added to the set only in linkDotPrimary.
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
bool m_explicitSuperNew = false; // Hit a "super.new" call inside a "new" function
AstNew* m_explicitSuperNewp = nullptr; // Hit a "super.new" call inside a "new" function
std::map<AstNode*, AstPin*> m_usedPins; // Pin used in this cell, map to duplicate
std::map<std::string, AstNodeModule*> m_modulesToRevisit; // Modules to revisit a second time
AstNode* m_lastDeferredp = nullptr; // Last node which requested a revisit of its module
@ -2354,12 +2354,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
return nullptr;
}
}
AstNodeStmt* addImplicitSuperNewCall(AstFunc* nodep) {
AstNodeStmt* addImplicitSuperNewCall(AstFunc* const nodep,
const AstClassExtends* const classExtendsp) {
// Returns the added node
FileLine* const fl = nodep->fileline();
AstNodeExpr* pinsp = nullptr;
if (classExtendsp->argsp()) pinsp = classExtendsp->argsp()->cloneTree(true);
AstNew* const newExprp = new AstNew{fl, pinsp};
newExprp->isImplicit(true);
AstDot* const superNewp
= new AstDot{fl, false, new AstParseRef{fl, VParseRefExp::PX_ROOT, "super"},
new AstNew{fl, nullptr}};
= new AstDot{fl, false, new AstParseRef{fl, VParseRefExp::PX_ROOT, "super"}, newExprp};
AstNodeStmt* const superNewStmtp = superNewp->makeStmt();
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
// super.new shall be the first statement (IEEE 1800-2023 8.15)
@ -3982,17 +3986,33 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
VL_RESTORER(m_curSymp);
VL_RESTORER(m_ftaskp);
VL_RESTORER(m_explicitSuperNewp);
{
m_ftaskp = nodep;
m_ds.m_dotSymp = m_curSymp = m_statep->getNodeSym(nodep);
const bool isNew = nodep->name() == "new";
if (isNew) m_explicitSuperNew = false;
if (isNew) m_explicitSuperNewp = nullptr;
iterateChildren(nodep);
if (isNew && !m_explicitSuperNew && m_statep->forParamed()) {
if (isNew) {
const AstClassExtends* const classExtendsp = VN_AS(m_modp, Class)->extendsp();
if (m_explicitSuperNewp && !m_explicitSuperNewp->isImplicit()
&& classExtendsp->argsp()) {
m_explicitSuperNewp->v3error(
"Explicit super.new not allowed with class "
"extends arguments (IEEE 1800-2023 8.17)\n"
<< m_explicitSuperNewp->warnMore() << "... Suggest remove super.new\n"
<< m_explicitSuperNewp->warnContextPrimary() << '\n'
<< classExtendsp->argsp()->warnOther()
<< "... Location of extends argument(s)\n"
<< classExtendsp->argsp()->warnContextSecondary());
}
if (classExtendsp && classExtendsp->classOrNullp()) {
AstNodeStmt* const superNewp = addImplicitSuperNewCall(VN_AS(nodep, Func));
iterate(superNewp);
if (!m_explicitSuperNewp && m_statep->forParamed()) {
AstNodeStmt* const superNewp
= addImplicitSuperNewCall(VN_AS(nodep, Func), classExtendsp);
UINFO(9, "created super new " << superNewp << endl);
iterate(superNewp);
}
}
}
}
@ -4356,12 +4376,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
LINKDOT_VISIT_START();
checkNoDot(nodep);
// Check if nodep represents a super.new call;
if (VN_IS(nodep->exprp(), New)) {
if (AstNew* const newExprp = VN_CAST(nodep->exprp(), New)) {
// in this case it was already linked, so it doesn't have a super reference
m_explicitSuperNew = true;
m_explicitSuperNewp = newExprp;
} else if (const AstDot* const dotp = VN_CAST(nodep->exprp(), Dot)) {
if (dotp->lhsp()->name() == "super" && VN_IS(dotp->rhsp(), New)) {
m_explicitSuperNew = true;
if (dotp->lhsp()->name() == "super") {
if (AstNew* const newExprp = VN_CAST(dotp->rhsp(), New)) {
m_explicitSuperNewp = newExprp;
}
}
}
iterateChildren(nodep);

View File

@ -147,6 +147,12 @@ private:
}
}
}
void visit(AstClassExtends* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
// Extend arguments were converted to super.new arguments in V3LinkDot
if (nodep->argsp()) pushDeletep(nodep->argsp()->unlinkFrBackWithNext());
iterateChildren(nodep);
}
void visit(AstConst* nodep) override {
if (nodep->user1SetOnce()) return; // Process once
UASSERT_OBJ(nodep->dtypep(), nodep, "No dtype");

View File

@ -7340,7 +7340,7 @@ classExtendsOne<classExtendsp>: // IEEE: part of class_declaration
$<scp>$ = $<scp>1; }
| class_typeExtImpList '(' list_of_argumentsE ')'
{ $$ = new AstClassExtends{$1->fileline(), $1, GRAMMARP->m_inImplements};
BBUNSUP($<fl>2, "Unsupported: 'extends' with class list_of_arguments");
$$->addArgsp($3);
$<scp>$ = $<scp>1; }
// // IEEE-2023: Added: yEXTENDS class_type '(' yDEFAULT ')'
| class_typeExtImpList '(' yDEFAULT ')'

View File

@ -1,8 +0,0 @@
%Error-UNSUPPORTED: t/t_class_extends_arg.v:14:25: Unsupported: 'extends' with 'default'
14 | class Cls1 extends Base1(default);
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_class_extends_arg.v:18:25: Unsupported: 'extends' with class list_of_arguments
18 | class Cls5 extends Base1(5);
| ^
%Error: Exiting due to

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 by Wilson Snyder. This program is free software; you
# Copyright 2025 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.
@ -9,8 +9,10 @@
import vltest_bootstrap
test.scenarios('linter')
test.scenarios('simulator')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.compile()
test.execute()
test.passes()

View File

@ -1,36 +1,44 @@
// 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.
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base1;
int s = 2;
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
class Base;
int m_s = 2;
function new(int def = 3);
s = def;
m_s = def;
endfunction
endclass
class Cls1 extends Base1(default);
// Gets new(int def)
class Cls5Exp extends Base(5);
int m_a = 11;
function new(int def = 42); // Explicit new
m_a = def;
endfunction
endclass
class Cls5 extends Base1(5);
// Gets new()
class Cls5Imp extends Base(5);
int m_a = 12;
// Implicit new
endclass
module t (/*AUTOARG*/);
module t ();
Cls5Exp ce;
Cls5Imp ci;
initial begin
Cls1 c1;
Cls1 c5;
c1 = new(57);
if (c1.s !== 57) $stop;
c5 = new;
if (c5.s !== 5) $stop;
ce = new(37);
`checkh(ce.m_s, 5);
`checkh(ce.m_a, 37);
ci = new;
`checkh(ci.m_s, 5);
`checkh(ci.m_a, 12);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,9 @@
%Error: t/t_class_extends_arg_super_bad.v:17:13: Explicit super.new not allowed with class extends arguments (IEEE 1800-2023 8.17)
: ... Suggest remove super.new
17 | super.new(33);
| ^~~
t/t_class_extends_arg_super_bad.v:14:25: ... Location of extends argument(s)
14 | class Cls5 extends Base(5);
| ^
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2025 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
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,20 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
class Base;
int m_s = 2;
function new(int def = 3);
m_s = def;
endfunction
endclass
class Cls5 extends Base(5);
int m_a;
function new(int def = 42);
super.new(33); // Bad, can't super.new with extends args
m_a = def;
endfunction
endclass

View File

@ -0,0 +1,5 @@
%Error-UNSUPPORTED: t/t_class_extends_default.v:14:25: Unsupported: 'extends' with 'default'
14 | class Cls1 extends Base1(default);
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('linter')
test.lint(fails=test.vlt_all, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,32 @@
// 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;
int s = 2;
function new(int def = 3);
s = def;
endfunction
endclass
class Cls1 extends Base1(default);
// Gets new(int def)
endclass
module t (/*AUTOARG*/);
initial begin
Cls1 c1;
Cls1 c5;
c1 = new(57);
if (c1.s !== 57) $stop;
c5 = new;
if (c5.s !== 5) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule