From c840ffb0ae8fe5cce69aba9b608e9e60af8668f9 Mon Sep 17 00:00:00 2001 From: Kamil Rakoczy Date: Mon, 17 Feb 2025 13:47:41 +0100 Subject: [PATCH] Support nested classes (#4178) (#5778) Signed-off-by: Kamil Rakoczy --- src/V3LinkDot.cpp | 43 +++++++++- src/V3WidthCommit.cpp | 10 ++- src/verilog.y | 3 +- test_regress/t/t_class_class.out | 5 -- test_regress/t/t_class_class.py | 4 +- test_regress/t/t_class_class.v | 3 + test_regress/t/t_class_compare.v | 14 ++++ test_regress/t/t_class_extends1.v | 43 ++++++++++ test_regress/t/t_class_extends2.v | 46 +++++++++++ test_regress/t/t_class_extends_param.v | 35 ++++++++ test_regress/t/t_class_extends_this.v | 35 +++++++- test_regress/t/t_class_extends_this3.v | 10 +++ test_regress/t/t_class_extern.v | 50 +++++++++++- test_regress/t/t_class_local.v | 53 ++++++++++++ test_regress/t/t_class_local_nested_bad.out | 9 +++ test_regress/t/t_class_local_nested_bad.py | 16 ++++ test_regress/t/t_class_local_nested_bad.v | 25 ++++++ test_regress/t/t_class_module.v | 11 +++ test_regress/t/t_class_nested.py | 18 +++++ test_regress/t/t_class_nested.v | 90 +++++++++++++++++++++ test_regress/t/t_class_new.v | 15 ++++ test_regress/t/t_class_param.v | 11 +++ test_regress/t/t_class_param_rewrite.v | 8 ++ test_regress/t/t_class_static_member.v | 8 +- test_regress/t/t_class_virtual.v | 24 ++++++ test_regress/t/t_implements_nested.py | 16 ++++ test_regress/t/t_implements_nested.v | 16 ++++ test_regress/t/t_implements_nested_bad.out | 7 +- test_regress/t/t_implements_nested_bad.v | 4 +- test_regress/t/t_semaphore_class_nested.py | 16 ++++ test_regress/t/t_semaphore_class_nested.v | 39 +++++++++ 31 files changed, 664 insertions(+), 23 deletions(-) delete mode 100644 test_regress/t/t_class_class.out create mode 100644 test_regress/t/t_class_local_nested_bad.out create mode 100755 test_regress/t/t_class_local_nested_bad.py create mode 100644 test_regress/t/t_class_local_nested_bad.v create mode 100755 test_regress/t/t_class_nested.py create mode 100644 test_regress/t/t_class_nested.v create mode 100755 test_regress/t/t_implements_nested.py create mode 100644 test_regress/t/t_implements_nested.v create mode 100755 test_regress/t/t_semaphore_class_nested.py create mode 100644 test_regress/t/t_semaphore_class_nested.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 9f77c8aad..b1b0b9223 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1013,6 +1013,7 @@ class LinkDotFindVisitor final : public VNVisitor { VL_RESTORER(m_modBlockNum); VL_RESTORER(m_modWithNum); VL_RESTORER(m_modArgNum); + VL_RESTORER(m_explicitNew); { UINFO(4, " Link Class: " << nodep << endl); VSymEnt* const upperSymp = m_curSymp; @@ -1176,12 +1177,39 @@ class LinkDotFindVisitor final : public VNVisitor { } // Change to appropriate package if extern declaration (vs definition) if (nodep->classOrPackagep()) { + // class-in-class + AstDot* const dotp = VN_CAST(nodep->classOrPackagep(), Dot); AstClassOrPackageRef* const cpackagerefp = VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef); - if (!cpackagerefp) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: extern function definition with class-in-class"); - } else { + if (dotp) { + AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, lhsp, false, + "External definition :: reference"); + AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class); + if (!lhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(lhsclassp); + upSymp = m_curSymp; + AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, rhsp, false, + "External definition :: reference"); + AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class); + if (!rhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(rhsclassp); + upSymp = m_curSymp; + if (!nodep->isExternDef()) { + // Move it to proper spot under the target class + nodep->unlinkFrBack(); + rhsclassp->addStmtsp(nodep); + nodep->isExternDef(true); // So we check there's a matching extern + nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); + } + } + } + } else if (cpackagerefp) { if (!cpackagerefp->classOrPackageSkipp()) { m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, false, "External definition :: reference"); @@ -1200,6 +1228,8 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); } } + } else { + v3fatalSrc("Unhandled extern function definition package"); } } // Set the class as package for iteration @@ -4007,6 +4037,11 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep << endl); checkNoDot(nodep); + AstClass* const topclassp = VN_CAST(m_modp, Class); + if (nodep->isInterfaceClass() && topclassp && topclassp->isInterfaceClass()) { + nodep->v3error("Interface class shall not be nested within another interface class." + " (IEEE 1800-2023 8.26)"); + } VL_RESTORER(m_curSymp); VL_RESTORER(m_modSymp); VL_RESTORER(m_modp); diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 5050fda8d..0318294e8 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -100,9 +100,15 @@ private: if (local || prot) { const auto refClassp = VN_CAST(m_modp, Class); const char* how = nullptr; - if (local && defClassp && refClassp != defClassp) { + // Inner nested classes can access `local` or `protected` members of their outer class + const auto nestedAccess = [refClassp](const AstClass*, const AstNode* memberp) { + return memberp == refClassp; + }; + if (local && defClassp + && ((refClassp != defClassp) && !(defClassp->existsMember(nestedAccess)))) { how = "'local'"; - } else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)) { + } else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp) + && !(defClassp->existsMember(nestedAccess))) { how = "'protected'"; } if (how) { diff --git a/src/verilog.y b/src/verilog.y index 3d66945f0..c1e5a51c4 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7354,8 +7354,7 @@ class_item: // ==IEEE: class_item | class_method { $$ = $1; } | class_constraint { $$ = $1; } // - | class_declaration - { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } + | class_declaration { $$ = $1; } | timeunits_declaration { $$ = $1; } | covergroup_declaration { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); } diff --git a/test_regress/t/t_class_class.out b/test_regress/t/t_class_class.out deleted file mode 100644 index 2ec79ad39..000000000 --- a/test_regress/t/t_class_class.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_class.v:12:4: Unsupported: class within class - 12 | class SubCls; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_class_class.py b/test_regress/t/t_class_class.py index 710a094ab..d4f986441 100755 --- a/test_regress/t/t_class_class.py +++ b/test_regress/t/t_class_class.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_class_class.v b/test_regress/t/t_class_class.v index bf1ae7da4..14177f768 100644 --- a/test_regress/t/t_class_class.v +++ b/test_regress/t/t_class_class.v @@ -31,5 +31,8 @@ module t (/*AUTOARG*/); if (c.imemberb != 20) $stop; if (c.sc.smembera != 30) $stop; if (c.sc.smemberb != 40) $stop; + + $write("*-* All Finished *-*\n"); + $finish; end endmodule diff --git a/test_regress/t/t_class_compare.v b/test_regress/t/t_class_compare.v index bcbb1842a..b20d435cd 100644 --- a/test_regress/t/t_class_compare.v +++ b/test_regress/t/t_class_compare.v @@ -11,6 +11,9 @@ `define check_ne(lhs, rhs) `check_comp(lhs, rhs, ==, 1'b0) `check_comp(lhs, rhs, !=, 1'b1) class Cls; + class InnerCls; + int j; + endclass int i; endclass @@ -22,14 +25,25 @@ module t; Cls a = new; Cls b = new; ExtendCls ext = new; + Cls::InnerCls ia = new; + Cls::InnerCls ib = new; + ExtendCls::InnerCls iext = new; `check_ne(a, b) `check_ne(a, ext) `check_ne(ext, a) + `check_ne(ia, ib) + `check_ne(ia, iext) + `check_ne(iext, ia) a = b; + ia = ib; `check_eq(a, b) + `check_eq(ia, ib) a = ext; + ia = iext; `check_eq(a, ext) `check_eq(ext, a) + `check_eq(ia, iext) + `check_eq(iext, ia) $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_extends1.v b/test_regress/t/t_class_extends1.v index c0f8ceeec..afe7878d9 100644 --- a/test_regress/t/t_class_extends1.v +++ b/test_regress/t/t_class_extends1.v @@ -5,21 +5,55 @@ // SPDX-License-Identifier: CC0-1.0 class Base0; + class BaseInnerOnly; + int inneronly; + function new(); + inneronly = 10; + if (inneronly != 10) $stop; + endfunction + endclass + + class BaseInnerOver; + int innerover; + function new(); + innerover = 10; + if (innerover != 10) $stop; + endfunction + endclass + int baseonly; int baseover; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; 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 + function void b_set_io(int v); innerover.innerover = v; endfunction + function int b_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass class Ext extends Base0; + class BaseInnerOver; + int innerover; + function new(); + innerover = 20; + if (innerover != 20) $stop; + endfunction + endclass + int baseover; int extonly; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; 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 + function void e_set_io(int v); innerover.innerover = v; endfunction + function int e_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass module t (/*AUTOARG*/); @@ -29,15 +63,24 @@ module t (/*AUTOARG*/); c.baseonly = 10; c.baseover = 20; c.extonly = 30; + c.inneronly.inneronly = 40; + c.innerover.innerover = 50; if (c.baseonly != 10) $stop; if (c.baseover != 20) $stop; if (c.extonly != 30) $stop; + if (c.inneronly.inneronly != 40) $stop; + if (c.innerover.innerover != 50) $stop; c.b_set_bo(100); c.e_set_bo(200); + c.b_set_io(300); + c.e_set_io(400); if (c.b_get_bo() != 100) $stop; if (c.e_get_bo() != 200) $stop; if (c.get_bo() != 200) $stop; + if (c.b_get_io() != 300) $stop; + if (c.e_get_io() != 400) $stop; + if (c.get_io() != 400) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends2.v b/test_regress/t/t_class_extends2.v index 8c30f0c6b..14d50907b 100644 --- a/test_regress/t/t_class_extends2.v +++ b/test_regress/t/t_class_extends2.v @@ -6,22 +6,59 @@ package Pkg; class Base0; + class BaseInnerOnly; + int inneronly; + function new(); + inneronly = 10; + if (inneronly != 10) $stop; + endfunction + endclass + + class BaseInnerOver; + int innerover; + function new(); + innerover = 10; + if (innerover != 10) $stop; + endfunction + endclass + int baseonly; int baseover; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; 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 + function void b_set_io(int v); innerover.innerover = v; endfunction + function int b_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass endpackage +// We need to import Base0, as verilator currently doesn't support +// multiple `::` references, but we would need to do that to reference +// `BaseInnerOnly` class inside `Ext` class. +import Pkg::Base0; class Ext extends Pkg::Base0; + class BaseInnerOver; + int innerover; + function new(); + innerover = 20; + if (innerover != 20) $stop; + endfunction + endclass int baseover; int extonly; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; 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 + function void e_set_io(int v); innerover.innerover = v; endfunction + function int e_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass module t (/*AUTOARG*/); @@ -31,15 +68,24 @@ module t (/*AUTOARG*/); c.baseonly = 10; c.baseover = 20; c.extonly = 30; + c.inneronly.inneronly = 40; + c.innerover.innerover = 50; if (c.baseonly != 10) $stop; if (c.baseover != 20) $stop; if (c.extonly != 30) $stop; + if (c.inneronly.inneronly != 40) $stop; + if (c.innerover.innerover != 50) $stop; c.b_set_bo(100); c.e_set_bo(200); + c.b_set_io(300); + c.e_set_io(400); if (c.b_get_bo() != 100) $stop; if (c.e_get_bo() != 200) $stop; if (c.get_bo() != 200) $stop; + if (c.b_get_io() != 300) $stop; + if (c.e_get_io() != 400) $stop; + if (c.get_io() != 400) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index f84fe1fa2..f8544aa80 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -8,26 +8,53 @@ module t (/*AUTOARG*/ ); class Foo; + class InnerFoo; + int y = 10; + function int get_y; + return y; + endfunction + function int get_30; + return 30; + endfunction + endclass + int x = 1; + InnerFoo foo = new; function int get_x; return x; endfunction function int get_3; return 3; endfunction + function InnerFoo get_foo; + return foo; + endfunction endclass class Bar #(type T=Foo) extends T; endclass class Baz; + class InnerFoo; + int y = 20; + function int get_y; + return y; + endfunction + function int get_40; + return 40; + endfunction + endclass int x = 2; + InnerFoo foo = new; function int get_x; return x; endfunction function int get_4; return 4; endfunction + function InnerFoo get_foo; + return foo; + endfunction endclass class ExtendBar extends Bar#(); @@ -71,6 +98,9 @@ module t (/*AUTOARG*/ function int get_x_of_item(int i); return q[i].x; endfunction + function int get_y_of_item(int i); + return q[i].get_foo().get_y(); + endfunction endclass Bar #() bar_foo_i; @@ -93,8 +123,12 @@ module t (/*AUTOARG*/ if (bar_foo_i.get_x() != 1) $stop; if (bar_foo_i.get_3() != 3) $stop; + if (bar_foo_i.get_foo().get_y() != 10) $stop; + if (bar_foo_i.get_foo().get_30() != 30) $stop; if (bar_baz_i.get_x() != 2) $stop; if (bar_baz_i.get_4() != 4) $stop; + if (bar_baz_i.get_foo().get_y() != 20) $stop; + if (bar_baz_i.get_foo().get_40() != 40) $stop; if (extend_bar_i.get_x() != 1) $stop; if (extend_bar_i.get_6() != 6) $stop; if (extend_bar_i.get_x() != 1) $stop; @@ -106,6 +140,7 @@ module t (/*AUTOARG*/ if (extend_extend_bar_i.get_x() != 1) $stop; if (extend_extend_bar_i.get_12() != 12) $stop; if (extend_foo_dict_i.get_x_of_item(1) != 1) $stop; + if (extend_foo_dict_i.get_y_of_item(1) != 10) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends_this.v b/test_regress/t/t_class_extends_this.v index fb185267d..00fd653b9 100644 --- a/test_regress/t/t_class_extends_this.v +++ b/test_regress/t/t_class_extends_this.v @@ -7,7 +7,20 @@ typedef class Cls; class Base; + class BaseInner; + int value = 10; + function void test; + if (value != 10) $stop; + if (this.value != 10) $stop; + value = 20; + if (value != 20) $stop; + this.value = 30; + if (value != 30) $stop; + endfunction + endclass + int value = 1; + BaseInner inner = new; function void test; if (value != 1) $stop; if (this.value != 1) $stop; @@ -19,7 +32,25 @@ class Base; endclass class Cls extends Base; + class BaseInner extends Base::BaseInner; + int value = 100; + function void test; + if (value != 100) $stop; + if (this.value != 100) $stop; + if (super.value != 10) $stop; + super.test(); + if (value != 100) $stop; + if (this.value != 100) $stop; + if (super.value != 30) $stop; + value = 200; + if (value != 200) $stop; + this.value = 300; + if (value != 300) $stop; + endfunction + endclass + int value = 20; + BaseInner inner = new; function void test; if (value != 20) $stop; if (this.value != 20) $stop; @@ -31,7 +62,9 @@ class Cls extends Base; super.value = 9; this.value = 29; if (super.value != 9) $stop; - if (value != 29) $stop;; + if (value != 29) $stop; + + inner.test(); endfunction endclass diff --git a/test_regress/t/t_class_extends_this3.v b/test_regress/t/t_class_extends_this3.v index 20a66e469..908276e58 100644 --- a/test_regress/t/t_class_extends_this3.v +++ b/test_regress/t/t_class_extends_this3.v @@ -7,15 +7,24 @@ typedef class Cls; class Base; + class Inner; + int value = 10; + function void testBaseInner; + if (value != 10) $stop; + endfunction + endclass int value = 1; + Inner inner = new; function void testBase; if (value != 1) $stop; + if (inner.value != 10) $stop; endfunction endclass class Cls extends Base; function void testDerived; if (value != 1) $stop; + if (inner.value != 10) $stop; endfunction endclass @@ -25,6 +34,7 @@ module t (/*AUTOARG*/); c = new; c.testBase(); c.testDerived(); + c.inner.testBaseInner(); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_extern.v b/test_regress/t/t_class_extern.v index 0cddfdc3f..3afa91045 100644 --- a/test_regress/t/t_class_extern.v +++ b/test_regress/t/t_class_extern.v @@ -13,6 +13,16 @@ class Cls; extern task ext_t_np; extern task ext_t_p(); extern task ext_t_i(int in); + class SubCls; + int value; + extern function int ext_f_np; + extern function int ext_f_p(); + extern function int ext_f_i(int in); + extern static function int get_10(); + extern task ext_t_np; + extern task ext_t_p(); + extern task ext_t_i(int in); + endclass endclass function int Cls::ext_f_np; @@ -42,15 +52,51 @@ task Cls::ext_t_i(int in); value = in; endtask +function int Cls::SubCls::ext_f_np; + return 10; +endfunction + +function int Cls::SubCls::ext_f_p(); + return value; +endfunction + +function int Cls::SubCls::ext_f_i(int in); + return in+10; +endfunction + +function int Cls::SubCls::get_10(); + return 10; +endfunction + +task Cls::SubCls::ext_t_np(); + $write("Cls::SubCls::ext_t_np\n"); +endtask +task Cls::SubCls::ext_t_p(); + $write("Cls::SubCls::ext_t_p\n"); +endtask +task Cls::SubCls::ext_t_i(int in); + if (in != 20) $stop; + value = in; +endtask + + module t (/*AUTOARG*/); initial begin Cls c = new; + Cls::SubCls subc = new; c.ext_t_i(2); - c.ext_t_np(); - c.ext_t_p(); if (c.ext_f_np() != 1) $stop; if (c.ext_f_p() != 2) $stop; if (c.ext_f_i(10) != 11) $stop; if (Cls::get_1() != 1) $stop; + subc.ext_t_i(20); + if (subc.ext_f_np() != 10) $stop; + if (subc.ext_f_p() != 20) $stop; + if (subc.ext_f_i(11) != 21) $stop; + if (Cls::SubCls::get_10() != 10) $stop; + subc.ext_t_np(); + subc.ext_t_p(); + c.ext_t_np(); + c.ext_t_p(); end endmodule diff --git a/test_regress/t/t_class_local.v b/test_regress/t/t_class_local.v index 0a5b6cf78..db9d9e975 100644 --- a/test_regress/t/t_class_local.v +++ b/test_regress/t/t_class_local.v @@ -31,6 +31,34 @@ class Cls; Cls::s_loc(); // Ok Cls::s_prot(); // Ok endtask + class InnerCls; + typedef enum {A = 10, B = 20, C = 30} en_t; + + int m_pub = 1; + local int m_loc = 2; + protected int m_prot = B; + task f_pub; endtask + local task f_loc; endtask + protected task f_prot; endtask + static task s_pub; endtask + static local task s_loc; endtask + static protected task s_prot; endtask + task check; + Cls o; + if (m_pub != 1) $stop; + if (m_loc != 2) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_loc(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_loc(); // Ok + s_prot(); // Ok + Cls::s_pub(); // Ok + Cls::s_loc(); // Ok + Cls::s_prot(); // Ok + endtask + endclass endclass class Ext extends Cls; @@ -44,27 +72,52 @@ class Ext extends Cls; Cls::s_pub(); // Ok Cls::s_prot(); // Ok endtask + class ExtInner extends Cls::InnerCls; + task check; + if (m_pub != 1) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_prot(); // Ok + Cls::InnerCls::s_pub(); // Ok + Cls::InnerCls::s_prot(); // Ok + endtask + endclass endclass module t (/*AUTOARG*/); const Cls mod_c = new; + const Cls::InnerCls imod_c = new; initial begin Cls c; + Cls::InnerCls i; Ext e; + Ext::ExtInner ei; if (c.A != 10) $stop; + if (i.A != 10) $stop; c = new; + i = new; e = new; + ei = new; if (c.m_pub != 1) $stop; + if (i.m_pub != 1) $stop; // if (mod_c.A != 10) $stop; + if (imod_c.A != 10) $stop; // c.check(); + i.check(); e.check(); + ei.check(); // Cls::s_pub(); // Ok + Cls::InnerCls::s_pub(); // Ok c.s_pub(); // Ok + i.s_pub(); // Ok e.s_pub(); // Ok + ei.s_pub(); // Ok // $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_local_nested_bad.out b/test_regress/t/t_class_local_nested_bad.out new file mode 100644 index 000000000..8f4b39314 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.out @@ -0,0 +1,9 @@ +%Error-ENCAPSULATED: t/t_class_local_nested_bad.v:14:22: 'name' is hidden as 'local' within this context (IEEE 1800-2023 8.18) + : ... note: In instance 't' + 14 | name = Node::name; + | ^~~~ + t/t_class_local_nested_bad.v:14:22: ... Location of definition + 9 | static local string name; + | ^~~~ + ... For error description see https://verilator.org/warn/ENCAPSULATED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_class_local_nested_bad.py b/test_regress/t/t_class_local_nested_bad.py new file mode 100755 index 000000000..710a094ab --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.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('simulator') + +test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_local_nested_bad.v b/test_regress/t/t_class_local_nested_bad.v new file mode 100644 index 000000000..f5e7b1155 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + static local string name; + endclass + + string name; + function new(); + name = Node::name; + endfunction +endclass + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_module.v b/test_regress/t/t_class_module.v index 60f043d9c..483026de2 100644 --- a/test_regress/t/t_class_module.v +++ b/test_regress/t/t_class_module.v @@ -7,8 +7,13 @@ module t (/*AUTOARG*/); class Cls; + class Inner; + int imemberinnera; + int imemberinnerb; + endclass int imembera; int imemberb; + Inner innermemberc; endclass : Cls class Dead; @@ -18,10 +23,16 @@ endclass Cls c; if (c != null) $stop; c = new; + if (c.innermemberc != null) $stop; + c.innermemberc = new; c.imembera = 10; c.imemberb = 20; + c.innermemberc.imemberinnera = 30; + c.innermemberc.imemberinnerb = 40; if (c.imembera != 10) $stop; if (c.imemberb != 20) $stop; + if (c.innermemberc.imemberinnera != 30) $stop; + if (c.innermemberc.imemberinnerb != 40) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_nested.py b/test_regress/t/t_class_nested.py new file mode 100755 index 000000000..49d728ea8 --- /dev/null +++ b/test_regress/t/t_class_nested.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--exe --main"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_nested.v b/test_regress/t/t_class_nested.v new file mode 100644 index 000000000..4a9e2e74d --- /dev/null +++ b/test_regress/t/t_class_nested.v @@ -0,0 +1,90 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + string name; + Node link; + + function new(); + name = "node"; + endfunction + endclass + + Node head; +endclass + +class NodeTree; + class Node; + int id; + Node link; + endclass + + Node root; +endclass + +// Based on IEEE 1800-2017 section 8.23 Nested classes +class Outer; + int outerProp; + local int outerLocalProp; + static int outerStaticProp; + static local int outerLocalStaticProp; + + class Inner; + function void innerMethod(Outer h); + outerStaticProp = 1; + outerLocalStaticProp = 1; + h.outerProp = 1; + h.outerLocalProp = 1; + endfunction + endclass +endclass + + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + NodeList::Node n1 = new; + NodeList::Node n2 = new; + NodeTree tr = new; + NodeTree::Node t1 = new; + NodeTree::Node t2 = new; + Outer o = new; + Outer::Inner i = new; + + i.innerMethod(o); + + if(o.outerProp != 1) $stop; + if(Outer::outerStaticProp != 1) $stop; + + if (n1.name != "node") $stop; + + n1.name = "n1"; + if (n1.name != "n1") $stop; + + n2.name = "n2"; + if (n2.name != "n2") $stop; + + n.head = n1; + n1.link = n2; + if (n.head.name != "n1") $stop; + if (n.head.link.name != "n2") $stop; + + t1.id = 1; + if (t1.id != 1) $stop; + + t2.id = 2; + if (t2.id != 2) $stop; + + tr.root = t1; + t1.link = t2; + if (tr.root.id != 1) $stop; + if (tr.root.link.id != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_new.v b/test_regress/t/t_class_new.v index 9d625c5a5..8f4ba17d5 100644 --- a/test_regress/t/t_class_new.v +++ b/test_regress/t/t_class_new.v @@ -11,6 +11,17 @@ class ClsNoArg; imembera = 5; if (other != 6) $stop; endfunction : new + class InnerNoArg; + const int imembera; + function new(); + int other = other_func(); + imembera = 5; + if (other != 6) $stop; + endfunction + function int other_func(); + return 6; + endfunction + endclass function int other_func(); return 6; endfunction @@ -52,6 +63,7 @@ module t (/*AUTOARG*/); ClsArg c2; Cls2Arg c3; Cls2Arg c4; + ClsNoArg::InnerNoArg c5 = new; c1 = new; if (c1.imembera != 5) $stop; @@ -72,6 +84,9 @@ module t (/*AUTOARG*/); if (c4.imembera != 6) $stop; if (c4.imemberb != 9) $stop; + c5 = new; + if (c5.imembera != 5) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index f2510ed1d..62ed7508a 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -42,6 +42,9 @@ class Cls #(parameter PBASE = 12); return PBASE; endfunction typedef enum { E_PBASE = PBASE } enum_t; + class ClsInner; + bit [PBASE-1:0] member; + endclass endclass typedef Cls#(8) Cls8_t; @@ -136,6 +139,8 @@ module t (/*AUTOARG*/); Cls c12; Cls #(.PBASE(4)) c4; Cls8_t c8; + Cls#()::ClsInner ci; + Cls#(8)::ClsInner ci8; Wrap #(.P(16)) w16; Wrap2 #(.P(32)) w32; SelfRefClassTypeParam src_logic; @@ -155,6 +160,8 @@ module t (/*AUTOARG*/); c12 = new; c4 = new; c8 = new; + ci = new; + ci8 = new; w16 = new; w32 = new; src_int = new; @@ -195,6 +202,10 @@ module t (/*AUTOARG*/); c4.member = 32'haaaaaaaa; c8.member = 32'haaaaaaaa; // verilator lint_on WIDTH + ci.member = 12'haaa; + ci8.member = 8'hff; + if (ci.member != 12'haaa) $stop; + if (ci8.member != 8'hff) $stop; if (c12.member != 12'haaa) $stop; if (c4.member != 4'ha) $stop; if (c12.get_member() != 12'haaa) $stop; diff --git a/test_regress/t/t_class_param_rewrite.v b/test_regress/t/t_class_param_rewrite.v index 17664ec71..b88019127 100644 --- a/test_regress/t/t_class_param_rewrite.v +++ b/test_regress/t/t_class_param_rewrite.v @@ -14,10 +14,18 @@ module test; if (enum_item.first().name() != "BAR_0") $stop; endfunction + class Inner1; + static function void print(); + E enum_item; + if (enum_item.first().name() != "BAR_0") + $stop; + endfunction + endclass endclass initial begin baz#(bar_e)::print(); + baz#(bar_e)::Inner1::print(); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_static_member.v b/test_regress/t/t_class_static_member.v index 1867b73b0..f6a0be448 100644 --- a/test_regress/t/t_class_static_member.v +++ b/test_regress/t/t_class_static_member.v @@ -8,6 +8,12 @@ `define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); class Cls; + class InnerCls; + static function int f_inner_cs_st(); + ++c_st; return c_st; + endfunction + endclass + int c_no = 2; //automatic int c_au = 2; // automatic not a legal keyword here static int c_st = 22; @@ -22,7 +28,6 @@ class Cls; static function int f_cs_st (); ++c_st; return c_st; endfunction - endclass module t (/*AUTOARG*/); @@ -43,6 +48,7 @@ module t (/*AUTOARG*/); v = b.f_c_st(); `checkh(v, 26); // v = Cls::f_cs_st(); `checkh(v, 27); + v = Cls::InnerCls::f_inner_cs_st(); `checkh(v, 28); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_virtual.v b/test_regress/t/t_class_virtual.v index 5b2e9511d..8c0b80920 100644 --- a/test_regress/t/t_class_virtual.v +++ b/test_regress/t/t_class_virtual.v @@ -8,18 +8,33 @@ virtual class VBase; virtual function int hello; return 1; endfunction + virtual class VNested; + virtual function int hello; + return 10; + endfunction + endclass endclass class VA extends VBase; virtual function int hello; return 2; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 20; + endfunction + endclass endclass class VB extends VBase; virtual function int hello; return 3; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 30; + endfunction + endclass endclass virtual class uvm_phase; @@ -59,18 +74,27 @@ module t; initial begin VA va = new; VB vb = new; + VA::VNested vna = new; + VB::VNested vnb = new; VBase b; + VBase::VNested bn; uvm_build_phase ph; ExtendsCls ec; if (va.hello() != 2) $stop; if (vb.hello() != 3) $stop; + if (vna.hello() != 20) $stop; + if (vnb.hello() != 30) $stop; b = va; + bn = vna; if (b.hello() != 2) $stop; + if (bn.hello() != 20) $stop; b = vb; + bn = vnb; if (b.hello() != 3) $stop; + if (bn.hello() != 30) $stop; ph = new; if (ph.get1() != 1) $stop; diff --git a/test_regress/t/t_implements_nested.py b/test_regress/t/t_implements_nested.py new file mode 100755 index 000000000..c2d985114 --- /dev/null +++ b/test_regress/t/t_implements_nested.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() + +test.passes() diff --git a/test_regress/t/t_implements_nested.v b/test_regress/t/t_implements_nested.v new file mode 100644 index 000000000..87fc8d603 --- /dev/null +++ b/test_regress/t/t_implements_nested.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, 2025 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + // IEEE 2023 only disallows nested interface inside another interface, not + // class + interface class good_can_nest; + endclass +endclass + +module t (/*AUTOARG*/); + Cls c; +endmodule diff --git a/test_regress/t/t_implements_nested_bad.out b/test_regress/t/t_implements_nested_bad.out index 1121c001f..fab3573e0 100644 --- a/test_regress/t/t_implements_nested_bad.out +++ b/test_regress/t/t_implements_nested_bad.out @@ -1,5 +1,4 @@ -%Error-UNSUPPORTED: t/t_implements_nested_bad.v:8:14: Unsupported: class within class - 8 | interface class bad_cannot_nest; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_implements_nested_bad.v:9:17: Interface class shall not be nested within another interface class. (IEEE 1800-2023 8.26) + 9 | interface class bad_cannot_nest; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_implements_nested_bad.v b/test_regress/t/t_implements_nested_bad.v index 119033fa3..be483fb27 100644 --- a/test_regress/t/t_implements_nested_bad.v +++ b/test_regress/t/t_implements_nested_bad.v @@ -5,7 +5,9 @@ // SPDX-License-Identifier: CC0-1.0 class Cls; - interface class bad_cannot_nest; + interface class inte; + interface class bad_cannot_nest; + endclass endclass endclass diff --git a/test_regress/t/t_semaphore_class_nested.py b/test_regress/t/t_semaphore_class_nested.py new file mode 100755 index 000000000..bbbcfc078 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.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('simulator') + +test.compile(verilator_flags2=["--binary --timing"]) + +test.passes() diff --git a/test_regress/t/t_semaphore_class_nested.v b/test_regress/t/t_semaphore_class_nested.v new file mode 100644 index 000000000..c18f65b45 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class semaphore_cls; + class InnerKeyClass; + int innerKeys; + function new(int keyCount = 0); + innerKeys = keyCount; + endfunction + endclass + // Test an implementation similar to what Verilator will do internally + InnerKeyClass m_keys; + function new(int keyCount = 0); + m_keys = new(keyCount); + endfunction + function void put(int keyCount = 1); + m_keys.innerKeys += keyCount; + endfunction + task get(int keyCount = 1); + wait (m_keys.innerKeys >= keyCount); + m_keys.innerKeys -= keyCount; + endtask + function int try_get(int keyCount = 1); + if (m_keys.innerKeys >= keyCount) begin + m_keys.innerKeys -= keyCount; + return 1; + end + else begin + return 0; + end + endfunction +endclass + +`define SEMAPHORE_T semaphore_cls + +`include "t_semaphore.v"