From ee5c0a29024dc6777491e78c913e9efc1bcdbe6f Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Mon, 24 Apr 2023 23:25:53 +0200 Subject: [PATCH] Support parameterized class references in extends statement (#4146) --- src/V3LinkDot.cpp | 209 ++++++++++++------------- test_regress/t/t_class_extends_param.v | 36 ++++- 2 files changed, 139 insertions(+), 106 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 150c5d4c2..e86b2df33 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -3287,7 +3287,7 @@ private: iterateChildren(nodep); } void visit(AstClass* nodep) override { - nodep->user3SetOnce(); + if (nodep->user3SetOnce()) return; UINFO(5, " " << nodep << endl); checkNoDot(nodep); VL_RESTORER(m_curSymp); @@ -3300,117 +3300,116 @@ private: m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep); m_modp = nodep; int next = 0; - for (AstNode* itemp = nodep->extendsp(); itemp; itemp = itemp->nextp()) { - if (AstClassExtends* const cextp = VN_CAST(itemp, ClassExtends)) { - // Replace abstract reference with hard pointer - // Will need later resolution when deal with parameters - if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) { - cextp->v3error("Multiple inheritance illegal on non-interface classes" - " (IEEE 1800-2017 8.13)"); - } - if (cextp->childDTypep() || cextp->dtypep()) { - // Already converted. Update symbol table to link unlinked members - importSymbolsFromExtended(nodep, cextp); - continue; - } - AstNode* cprp = cextp->classOrPkgsp(); - VSymEnt* lookSymp = m_curSymp; - if (AstDot* const dotp = VN_CAST(cextp->classOrPkgsp(), Dot)) { - dotp->user3(true); - if (AstClassOrPackageRef* lookNodep - = VN_CAST(dotp->lhsp(), ClassOrPackageRef)) { - iterate(lookNodep); - cprp = dotp->rhsp(); - lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep()); - } else { - dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE - " using non-class under dot"); - } - } - AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef); - if (VL_UNCOVERABLE(!cpackagerefp)) { - // Linking the extend gives an error before this is hit - cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + for (AstClassExtends* cextp = nodep->extendsp(); cextp; + cextp = VN_AS(cextp->nextp(), ClassExtends)) { + // Replace abstract reference with hard pointer + // Will need later resolution when deal with parameters + if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) { + cextp->v3error("Multiple inheritance illegal on non-interface classes" + " (IEEE 1800-2017 8.13)"); + } + if (cextp->childDTypep() || cextp->dtypep()) { + // Already converted. Update symbol table to link unlinked members. + // Base class has to be visited in a case if its extends statement + // needs to be handled. Recursive inheritance was already checked. + iterate(cextp->classp()); + importSymbolsFromExtended(nodep, cextp); + continue; + } + AstNode* cprp = cextp->classOrPkgsp(); + VSymEnt* lookSymp = m_curSymp; + if (AstDot* const dotp = VN_CAST(cprp, Dot)) { + dotp->user3(true); + if (AstClassOrPackageRef* lookNodep + = VN_CAST(dotp->lhsp(), ClassOrPackageRef)) { + iterate(lookNodep); + cprp = dotp->rhsp(); + lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep()); } else { - VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name()); - if (foundp) { - AstClassRefDType* classRefDtypep = nullptr; - AstClass* classp = VN_CAST(foundp->nodep(), Class); - if (classp) { - if (classp != nodep) { - // Case with recursive inheritance is handled later in this - // function - iterate(classp); - } - if (m_statep->forPrimary() - && m_extendsParam.find(classp) != m_extendsParam.end()) { - // Has a parameter as its base class - m_extendsParam.insert(nodep); - m_insideClassExtParam = true; - } - AstPin* paramsp = cpackagerefp->paramsp(); - if (paramsp) paramsp = paramsp->cloneTree(true); - classRefDtypep - = new AstClassRefDType{nodep->fileline(), classp, paramsp}; - } else if (AstParamTypeDType* const paramp - = VN_CAST(foundp->nodep(), ParamTypeDType)) { - if (m_statep->forPrimary()) { - // Extending has to be handled after V3Param.cpp - m_extendsParam.insert(nodep); - m_insideClassExtParam = true; - continue; - } else { - AstNodeDType* const paramTypep = paramp->subDTypep(); - classRefDtypep - = VN_CAST(paramTypep->cloneTree(false), ClassRefDType); - if (!classRefDtypep) { - paramTypep->v3error( - "Attempting to extend using non-class"); - } else { - classp = classRefDtypep->classp(); - } - } - } else { - cextp->v3warn(E_UNSUPPORTED, - "Unsupported: " << foundp->nodep()->prettyTypeName() - << " in AstClassExtends"); + dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE + " using non-class under dot"); + } + } + AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef); + if (VL_UNCOVERABLE(!cpackagerefp)) { + // Linking the extend gives an error before this is hit + cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + } else { + VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name()); + if (foundp) { + AstClassRefDType* classRefDtypep = nullptr; + AstClass* classp = VN_CAST(foundp->nodep(), Class); + if (classp) { + if (classp != nodep) { + // Case with recursive inheritance is handled later in this + // function + iterate(classp); } - - if (classp) { - UINFO(8, "Import to " << nodep << " from export class " << classp - << endl); - if (classp == nodep) { - cextp->v3error("Attempting to extend class " - << nodep->prettyNameQ() << " from itself"); - } else if (cextp->isImplements() && !classp->isInterfaceClass()) { - cextp->v3error( - "Attempting to implement from non-interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'extends'"); - } else if (!cextp->isImplements() && !nodep->isInterfaceClass() - && classp->isInterfaceClass()) { - cextp->v3error("Attempting to extend from interface class " - << classp->prettyNameQ() << '\n' - << "... Suggest use 'implements'"); + if (m_statep->forPrimary() + && m_extendsParam.find(classp) != m_extendsParam.end()) { + // Has a parameter as its base class + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + AstPin* paramsp = cpackagerefp->paramsp(); + if (paramsp) paramsp = paramsp->cloneTree(true); + classRefDtypep + = new AstClassRefDType{nodep->fileline(), classp, paramsp}; + } else if (AstParamTypeDType* const paramp + = VN_CAST(foundp->nodep(), ParamTypeDType)) { + if (m_statep->forPrimary()) { + // Extending has to be handled after V3Param.cpp + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + continue; + } else { + AstNodeDType* const paramTypep = paramp->subDTypep(); + classRefDtypep + = VN_CAST(paramTypep->cloneTree(false), ClassRefDType); + if (!classRefDtypep) { + paramTypep->v3error("Attempting to extend using non-class"); } else { - cextp->childDTypep(classRefDtypep); - classp->isExtended(true); - nodep->isExtended(true); - importSymbolsFromExtended(nodep, cextp); - VL_DO_DANGLING( - cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(), - cpackagerefp); + classp = classRefDtypep->classp(); } } } else { - const string suggest = m_statep->suggestSymFallback( - m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); - cpackagerefp->v3error( - "Class for '" - << cextp->verilogKwd() // extends/implements - << "' not found: " << cpackagerefp->prettyNameQ() << '\n' - << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); + cextp->v3warn(E_UNSUPPORTED, + "Unsupported: " << foundp->nodep()->prettyTypeName() + << " in AstClassExtends"); } + + if (classp) { + UINFO(8, "Import to " << nodep << " from export class " << classp + << endl); + if (classp == nodep) { + cextp->v3error("Attempting to extend class " + << nodep->prettyNameQ() << " from itself"); + } else if (cextp->isImplements() && !classp->isInterfaceClass()) { + cextp->v3error("Attempting to implement from non-interface class " + << classp->prettyNameQ() << '\n' + << "... Suggest use 'extends'"); + } else if (!cextp->isImplements() && !nodep->isInterfaceClass() + && classp->isInterfaceClass()) { + cextp->v3error("Attempting to extend from interface class " + << classp->prettyNameQ() << '\n' + << "... Suggest use 'implements'"); + } else { + cextp->childDTypep(classRefDtypep); + classp->isExtended(true); + nodep->isExtended(true); + importSymbolsFromExtended(nodep, cextp); + VL_DO_DANGLING(cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(), + cpackagerefp); + } + } + } else { + const string suggest = m_statep->suggestSymFallback( + m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); + cpackagerefp->v3error( + "Class for '" + << cextp->verilogKwd() // extends/implements + << "' not found: " << cpackagerefp->prettyNameQ() << '\n' + << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); } } } diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index cdec1c149..aaaedce8a 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -39,17 +39,51 @@ module t (/*AUTOARG*/ endfunction endclass + class ExtendBar1 extends Bar #(Foo); + function int get_x; + return super.get_x(); + endfunction + function int get_6; + return 2 * get_3(); + endfunction + endclass + + class ExtendBarBaz extends Bar #(Baz); + function int get_x; + return super.get_x(); + endfunction + function int get_8; + return 2 * get_4(); + endfunction + endclass + + class ExtendExtendBar extends ExtendBar; + function int get_12; + return 4 * get_3(); + endfunction + endclass + Bar #() bar_foo_i; Bar #(Baz) bar_baz_i; ExtendBar extend_bar_i; + ExtendBar1 extend_bar1_i; + ExtendBarBaz extend_bar_baz_i; + ExtendExtendBar extend_extend_bar_i; initial begin bar_foo_i = new; bar_baz_i = new; extend_bar_i = new; + extend_bar1_i = new; + extend_bar_baz_i = new; + extend_extend_bar_i = new; if (bar_foo_i.get_x() == 1 && bar_foo_i.get_3() == 3 && bar_baz_i.get_x() == 2 && bar_baz_i.get_4() == 4 && - extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6) begin + extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 && + extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 && + extend_bar1_i.get_x() == 1 && extend_bar1_i.get_6() == 6 && + extend_bar_baz_i.get_x() == 2 && extend_bar_baz_i.get_8() == 8 && + extend_extend_bar_i.get_x() == 1 && extend_extend_bar_i.get_12() == 12) begin $write("*-* All Finished *-*\n"); $finish; end