From 84a46939b348836ab8abbd79f61386e403997720 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 20 Apr 2023 13:11:35 +0200 Subject: [PATCH] Fix class extend param references (#4136) Signed-off-by: Ryszard Rozak --- src/V3LinkDot.cpp | 64 +++++++++++--------------- src/V3Param.cpp | 18 ++++---- test_regress/t/t_class_extends_param.v | 4 +- test_regress/t/t_class_param_type.v | 28 +++++++++-- 4 files changed, 64 insertions(+), 50 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 79de07076..150c5d4c2 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2035,6 +2035,8 @@ private: bool m_inSens = false; // True if in senitem std::set m_ifClassImpNames; // Names imported from interface class std::set m_extendsParam; // Classes that has a parameter as its super class + bool m_insideClassExtParam = false; // Inside a class that extends a parameter. + // It may be set only in linkDotPrimary. struct DotStates { DotPosition m_dotPos; // Scope part of dotted resolution @@ -2238,26 +2240,6 @@ private: if (classp->isInterfaceClass()) importImplementsClass(nodep, srcp, classp); if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp); } - void visitGlobalClassParams(AstClass* const nodep) { - // Parameters from port parameter list are at the beginning of the members list, so if a - // node, that isn't a parameter, is encountered, all nodes after aren't global parameters - // too. - for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { - // Global parameters are at the beginning of the members list, so if a node, that isn't - // a global parameter, is encountered, all nodes after it aren't global parameters too. - bool globalParam = false; - if (const AstParamTypeDType* const parp = VN_CAST(stmtp, ParamTypeDType)) { - if (parp->isGParam()) globalParam = true; - } else if (const AstVar* const varp = VN_CAST(stmtp, Var)) { - if (varp->isGParam()) globalParam = true; - } - if (globalParam) { - iterate(stmtp); - } else { - break; - } - } - } // VISITs void visit(AstNetlist* nodep) override { @@ -2424,12 +2406,17 @@ private: nodep->v3error("'super' used on non-extended class (IEEE 1800-2017 8.15)"); m_ds.m_dotErr = true; } else { - const auto cextp = classp->extendsp(); - UASSERT_OBJ(cextp, nodep, "Bad super extends link"); - const auto sclassp = cextp->classp(); - UASSERT_OBJ(sclassp, nodep, "Bad superclass"); - m_ds.m_dotSymp = m_statep->getNodeSym(sclassp); - UINFO(8, " super. " << m_ds.ascii() << endl); + if (m_statep->forPrimary() + && m_extendsParam.find(classp) != m_extendsParam.end()) { + m_ds.m_unresolvedClass = true; + } else { + const auto cextp = classp->extendsp(); + UASSERT_OBJ(cextp, nodep, "Bad super extends link"); + const auto sclassp = cextp->classp(); + UASSERT_OBJ(sclassp, nodep, "Bad superclass"); + m_ds.m_dotSymp = m_statep->getNodeSym(sclassp); + UINFO(8, " super. " << m_ds.ascii() << endl); + } } } } else if (VN_IS(nodep->lhsp(), ClassOrPackageRef)) { @@ -2798,8 +2785,10 @@ private: } } } - // - if (!ok) { + // Don't throw error ifthe reference is inside a class that extends a param, because + // some members can't be linked in such a case. m_insideClassExtParam may be true only + // in the first stage of linking. + if (!ok && !m_insideClassExtParam) { // Cells/interfaces can't be implicit const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false; const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !isCell); @@ -3110,6 +3099,10 @@ private: nodep->taskp(taskp); nodep->classOrPackagep(foundp->classOrPackagep()); UINFO(7, " Resolved " << nodep << endl); // Also prints taskp + } else if (m_insideClassExtParam) { + // The reference may point to a method declared in a super class, which is proved + // by a parameter. In such a case, it can't be linked at the first stage. + return; } else { // Note ParseRef has similar error handling/message output UINFO(7, " ErrFtask curSymp=se" << cvtToHex(m_curSymp) << " dotSymp=se" @@ -3300,6 +3293,7 @@ private: VL_RESTORER(m_curSymp); VL_RESTORER(m_modSymp); VL_RESTORER(m_ifClassImpNames); + VL_RESTORER(m_insideClassExtParam); { m_ds.init(m_curSymp); // Until overridden by a SCOPE @@ -3352,10 +3346,7 @@ private: && m_extendsParam.find(classp) != m_extendsParam.end()) { // Has a parameter as its base class m_extendsParam.insert(nodep); - // Link global parameters. They are declared before the extends - // statement, so should be unrelated to the base class - visitGlobalClassParams(nodep); - return; + m_insideClassExtParam = true; } AstPin* paramsp = cpackagerefp->paramsp(); if (paramsp) paramsp = paramsp->cloneTree(true); @@ -3364,13 +3355,12 @@ private: } else if (AstParamTypeDType* const paramp = VN_CAST(foundp->nodep(), ParamTypeDType)) { if (m_statep->forPrimary()) { - // Extending has to be handled after V3Param.cpp, but the type - // reference has to be visited - iterate(paramp); + // Extending has to be handled after V3Param.cpp m_extendsParam.insert(nodep); - return; + m_insideClassExtParam = true; + continue; } else { - AstNodeDType* const paramTypep = paramp->getChildDTypep(); + AstNodeDType* const paramTypep = paramp->subDTypep(); classRefDtypep = VN_CAST(paramTypep->cloneTree(false), ClassRefDType); if (!classRefDtypep) { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 977a0a803..b1324d5a8 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -949,16 +949,18 @@ class ParamVisitor final : public VNVisitor { // Process once; note user5 will be cleared on specialization, so we will do the // specialized module if needed - if (modp->user5SetOnce()) continue; + if (!modp->user5SetOnce()) { - // TODO: this really should be an assert, but classes and hier_blocks are special... - if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName()); + // TODO: this really should be an assert, but classes and hier_blocks are + // special... + if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName()); - // Iterate the body - { - VL_RESTORER(m_modp); - m_modp = modp; - iterateChildren(modp); + // Iterate the body + { + VL_RESTORER(m_modp); + m_modp = modp; + iterateChildren(modp); + } } // Process interface cells, then non-interface cells, which may reference an interface diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index 0d5e8cf36..cdec1c149 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -30,7 +30,7 @@ module t (/*AUTOARG*/ endfunction endclass - class ExtendBar extends Bar; + class ExtendBar extends Bar#(); function int get_x; return super.get_x(); endfunction @@ -39,7 +39,7 @@ module t (/*AUTOARG*/ endfunction endclass - Bar bar_foo_i; + Bar #() bar_foo_i; Bar #(Baz) bar_baz_i; ExtendBar extend_bar_i; diff --git a/test_regress/t/t_class_param_type.v b/test_regress/t/t_class_param_type.v index f636b4336..86b1d8f67 100644 --- a/test_regress/t/t_class_param_type.v +++ b/test_regress/t/t_class_param_type.v @@ -52,7 +52,7 @@ class Foo #(type IF=Empty) extends IF; int a = 1; endclass -class Bar #(type A=int, type B=A) extends Foo; +class Bar #(type A=int, type B=A) extends Foo#(); function int get_size_A; return $bits(A); endfunction @@ -64,16 +64,32 @@ endclass class Empty2; endclass -class Baz #(type T=Empty2) extends Foo; +class Baz #(type T=Empty2) extends Foo#(); endclass -class Getter1 extends Baz; +class Getter1 extends Baz#(); function int get_1; foo_t f = new; return f.a; endfunction endclass +class MyInt1; + int x = 1; +endclass + +class MyInt2; + int x = 2; +endclass + +class ExtendsMyInt #(type T=MyInt1) extends T; + typedef ExtendsMyInt#(T) this_type; + function int get_this_type_x; + this_type t = new; + return t.x; + endfunction +endclass + module t (/*AUTOARG*/); initial begin @@ -93,6 +109,9 @@ module t (/*AUTOARG*/); automatic Getter1 getter1 = new; + automatic ExtendsMyInt#() ext1 = new; + automatic ExtendsMyInt#(MyInt2) ext2 = new; + typedef bit my_bit_t; Bar#(.A(my_bit_t)) bar_a_bit = new; Bar#(.B(my_bit_t)) bar_b_bit = new; @@ -123,6 +142,9 @@ module t (/*AUTOARG*/); if (getter1.get_1() != 1) $stop; + if (ext1.get_this_type_x() != 1) $stop; + if (ext2.get_this_type_x() != 2) $stop; + $write("*-* All Finished *-*\n"); $finish; end