From 7c2b1971a4a5ad8c4c62866e0c9aa865d2a8bd82 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 8 Apr 2025 22:09:40 -0400 Subject: [PATCH] Support class extends with arguments. --- Changes | 1 + src/V3AstNodeExpr.h | 3 ++ src/V3AstNodeOther.h | 1 + src/V3LinkDot.cpp | 46 ++++++++++++++----- src/V3WidthCommit.cpp | 6 +++ src/verilog.y | 2 +- test_regress/t/t_class_extends_arg.out | 8 ---- test_regress/t/t_class_extends_arg.py | 8 ++-- test_regress/t/t_class_extends_arg.v | 44 ++++++++++-------- .../t/t_class_extends_arg_super_bad.out | 9 ++++ .../t/t_class_extends_arg_super_bad.py | 16 +++++++ .../t/t_class_extends_arg_super_bad.v | 20 ++++++++ test_regress/t/t_class_extends_default.out | 5 ++ test_regress/t/t_class_extends_default.py | 16 +++++++ test_regress/t/t_class_extends_default.v | 32 +++++++++++++ 15 files changed, 175 insertions(+), 42 deletions(-) delete mode 100644 test_regress/t/t_class_extends_arg.out create mode 100644 test_regress/t/t_class_extends_arg_super_bad.out create mode 100755 test_regress/t/t_class_extends_arg_super_bad.py create mode 100644 test_regress/t/t_class_extends_arg_super_bad.v create mode 100644 test_regress/t/t_class_extends_default.out create mode 100755 test_regress/t/t_class_extends_default.py create mode 100644 test_regress/t/t_class_extends_default.v diff --git a/Changes b/Changes index e3e210a7e..4098e33b6 100644 --- a/Changes +++ b/Changes @@ -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). diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 2d4299c4b..899319811 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -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 diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 5070c34c3..0c3b10859 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -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 diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 6383f0e72..825da71d1 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -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 m_usedPins; // Pin used in this cell, map to duplicate std::map 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); diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 0318294e8..4b9bbeb12 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -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"); diff --git a/src/verilog.y b/src/verilog.y index c19b23b94..22fe5e1cb 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7340,7 +7340,7 @@ classExtendsOne: // IEEE: part of class_declaration $$ = $1; } | class_typeExtImpList '(' list_of_argumentsE ')' { $$ = new AstClassExtends{$1->fileline(), $1, GRAMMARP->m_inImplements}; - BBUNSUP($2, "Unsupported: 'extends' with class list_of_arguments"); + $$->addArgsp($3); $$ = $1; } // // IEEE-2023: Added: yEXTENDS class_type '(' yDEFAULT ')' | class_typeExtImpList '(' yDEFAULT ')' diff --git a/test_regress/t/t_class_extends_arg.out b/test_regress/t/t_class_extends_arg.out deleted file mode 100644 index a1ec3e818..000000000 --- a/test_regress/t/t_class_extends_arg.out +++ /dev/null @@ -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 diff --git a/test_regress/t/t_class_extends_arg.py b/test_regress/t/t_class_extends_arg.py index 30c3d4f77..f989a35fb 100755 --- a/test_regress/t/t_class_extends_arg.py +++ b/test_regress/t/t_class_extends_arg.py @@ -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() diff --git a/test_regress/t/t_class_extends_arg.v b/test_regress/t/t_class_extends_arg.v index c32818463..cca83f85a 100644 --- a/test_regress/t/t_class_extends_arg.v +++ b/test_regress/t/t_class_extends_arg.v @@ -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 diff --git a/test_regress/t/t_class_extends_arg_super_bad.out b/test_regress/t/t_class_extends_arg_super_bad.out new file mode 100644 index 000000000..8b163b5e4 --- /dev/null +++ b/test_regress/t/t_class_extends_arg_super_bad.out @@ -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 diff --git a/test_regress/t/t_class_extends_arg_super_bad.py b/test_regress/t/t_class_extends_arg_super_bad.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_class_extends_arg_super_bad.py @@ -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() diff --git a/test_regress/t/t_class_extends_arg_super_bad.v b/test_regress/t/t_class_extends_arg_super_bad.v new file mode 100644 index 000000000..b96cf5264 --- /dev/null +++ b/test_regress/t/t_class_extends_arg_super_bad.v @@ -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 diff --git a/test_regress/t/t_class_extends_default.out b/test_regress/t/t_class_extends_default.out new file mode 100644 index 000000000..c9f2d131a --- /dev/null +++ b/test_regress/t/t_class_extends_default.out @@ -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 diff --git a/test_regress/t/t_class_extends_default.py b/test_regress/t/t_class_extends_default.py new file mode 100755 index 000000000..30c3d4f77 --- /dev/null +++ b/test_regress/t/t_class_extends_default.py @@ -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() diff --git a/test_regress/t/t_class_extends_default.v b/test_regress/t/t_class_extends_default.v new file mode 100644 index 000000000..b66d037bd --- /dev/null +++ b/test_regress/t/t_class_extends_default.v @@ -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