diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index fc998872b..960f10d09 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -809,6 +809,8 @@ class AstClassExtends final : public AstNode { // @astgen op1 := childDTypep : Optional[AstNodeDType] // @astgen op2 := classOrPkgsp : Optional[AstNode] const bool m_isImplements = false; // class implements + bool m_parameterized = false; // has parameters in its statement + public: AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements) : ASTGEN_SUPER_ClassExtends(fl) @@ -819,8 +821,12 @@ public: void dump(std::ostream& str) const override; bool hasDType() const override { return true; } string verilogKwd() const override { return isImplements() ? "implements" : "extends"; } - AstClass* classp() const; // Class being extended (after link) + // Class being extended (after link and instantiation if needed) + AstClass* classOrNullp() const; + AstClass* classp() const; // Like above, but throws error if nulll bool isImplements() const { return m_isImplements; } + void parameterized(bool flag) { m_parameterized = flag; } + bool parameterized() const { return m_parameterized; } }; class AstClocking final : public AstNode { // Parents: MODULE diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index 46f5e53d7..0f3fa80a4 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1501,13 +1501,20 @@ void AstClassExtends::dump(std::ostream& str) const { this->AstNode::dump(str); if (isImplements()) str << " [IMPLEMENTS]"; } -AstClass* AstClassExtends::classp() const { - const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType); - if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.' - refp = VN_AS(childDTypep(), ClassRefDType); +AstClass* AstClassExtends::classOrNullp() const { + const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep(); + const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType); + if (refp && !refp->paramsp()) { + // Class already resolved + return refp->classp(); + } else { + return nullptr; } - UASSERT_OBJ(refp, this, "class extends non-ref"); - return refp->classp(); +} +AstClass* AstClassExtends::classp() const { + AstClass* const clsp = classOrNullp(); + UASSERT_OBJ(clsp, this, "Extended class is unresolved"); + return clsp; } void AstClassRefDType::dump(std::ostream& str) const { this->AstNodeDType::dump(str); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index ca1965964..dd39277b6 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2020,9 +2020,10 @@ private: int m_modportNum = 0; // Uniqueify modport numbers 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. + std::set m_extendsParam; // Classes that have a parameterized super class + // (except the default instances) + // They are added to the set only in linkDotPrimary. + bool m_insideClassExtParam = false; // Inside a class from m_extendsParam struct DotStates { DotPosition m_dotPos; // Scope part of dotted resolution @@ -3273,6 +3274,86 @@ private: // No checknodot(nodep), visit(AstScope) will check for LambdaArgRef iterateChildren(nodep); } + void visit(AstClassExtends* nodep) override { + // Resolve the symbol and get the class. + // If it is a parameterized case, the class will be resolved after V3Param.cpp + if (nodep->user3SetOnce()) return; + // If the class is resolved, there is nothing more to do + if (nodep->classOrNullp()) return; + if (m_statep->forPrimary()) { + AstNode* cprp = nodep->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 { + 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 + nodep->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE + return; + } + VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name()); + if (foundp) { + if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) { + AstPin* paramsp = cpackagerefp->paramsp(); + if (paramsp) { + paramsp = paramsp->cloneTree(true); + nodep->parameterized(true); + } + nodep->childDTypep(new AstClassRefDType{nodep->fileline(), classp, paramsp}); + // Link pins + iterate(nodep->childDTypep()); + } else if (AstParamTypeDType* const paramp + = VN_CAST(foundp->nodep(), ParamTypeDType)) { + AstRefDType* const refParamp + = new AstRefDType{nodep->fileline(), paramp->name()}; + refParamp->refDTypep(paramp); + nodep->childDTypep(refParamp); + nodep->parameterized(true); + } else { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported: " << foundp->nodep()->prettyTypeName() + << " in AstClassExtends"); + return; + } + } else { + const string suggest = m_statep->suggestSymFallback( + m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{}); + cpackagerefp->v3error( + "Class for '" << nodep->verilogKwd() // extends/implements + << "' not found: " << cpackagerefp->prettyNameQ() << '\n' + << (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest)); + return; + } + if (!nodep->childDTypep()) nodep->v3error("Attempting to extend using non-class"); + nodep->classOrPkgsp()->unlinkFrBack()->deleteTree(); + } else { + // Probably a parameter + if (AstRefDType* const refp = VN_CAST(nodep->childDTypep(), RefDType)) { + if (AstClassRefDType* classRefp = VN_CAST(refp->skipRefp(), ClassRefDType)) { + // Resolved to a class reference. + refp->replaceWith(classRefp->cloneTree(false)); + } else { + // Unable to resolve the ref type to a class reference. + // Get the value of type parameter passed to the class instance, + // to print the helpful error message. + const AstNodeDType* typep = refp->refDTypep(); + if (const AstParamTypeDType* const paramp = VN_CAST(typep, ParamTypeDType)) { + typep = paramp->subDTypep(); + } + typep->v3error("Attempting to extend using non-class"); + } + } + } + } void visit(AstClass* nodep) override { if (nodep->user3SetOnce()) return; UINFO(5, " " << nodep << endl); @@ -3295,110 +3376,50 @@ private: cextp->v3error("Multiple inheritance illegal on non-interface classes" " (IEEE 1800-2017 8.13)"); } - if (cextp->childDTypep() || cextp->dtypep()) { + iterate(cextp); + if (m_statep->forPrimary()) { + if (cextp->parameterized()) { + // Parameters in extends statement. + // The class can't be resolved in the current pass. + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + if (AstClassRefDType* const classRefp + = VN_CAST(cextp->childDTypep(), ClassRefDType)) { + AstClass* const classp = classRefp->classp(); + if (classp != nodep) iterate(classp); + if (m_extendsParam.find(classp) != m_extendsParam.end()) { + // One of its super classes has parameters in extends statement. + // Some links may not be resolved in the first pass. + m_extendsParam.insert(nodep); + m_insideClassExtParam = true; + } + } + } + + if (AstClass* const classp = cextp->classOrNullp()) { // 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()); + 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'"); + } + classp->isExtended(true); + nodep->isExtended(true); + iterate(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 { - 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 (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"); - } - - 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)); - } - } } m_ds.m_dotSymp = m_curSymp; @@ -3491,6 +3512,8 @@ private: nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); return; + } else if (m_insideClassExtParam) { + return; } else { if (foundp) UINFO(1, "Found sym node: " << foundp->nodep() << endl); nodep->v3error("Can't find typedef: " << nodep->prettyNameQ()); diff --git a/test_regress/t/t_class_param_extends.v b/test_regress/t/t_class_param_extends.v index 1c1078ea7..e63ccb0b5 100644 --- a/test_regress/t/t_class_param_extends.v +++ b/test_regress/t/t_class_param_extends.v @@ -21,16 +21,49 @@ endclass typedef Cls#(8) Cls8_t; +class Getter1; + function int get_int; + return 1; + endfunction +endclass + +class Getter2; + function int get_int; + return 2; + endfunction +endclass + +class Foo #(type T=Getter1); + int x; + function new(int y); + x = y; + endfunction +endclass + +class Bar #(type S=Getter2) extends Foo#(S); + T field; + function new(int y); + super.new(y); + field = new; + endfunction + + function int get_field_int; + return field.get_int(); + endfunction +endclass + // See also t_class_param_mod.v module t (/*AUTOARG*/); Cls #(.P(4)) c4; Cls8_t c8; + Bar b; initial begin c4 = new; c8 = new; + b = new(1); if (c4.PBASE != 4) $stop; if (c8.PBASE != 8) $stop; if (c4.get_p() != 4) $stop; @@ -46,6 +79,9 @@ module t (/*AUTOARG*/); $display("c4 = %s", $sformatf("%p", c4)); if ($sformatf("%p", c4) != "'{member:'ha}") $stop; + if (b.x != 1) $stop; + if (b.get_field_int() != 2) $stop; + $write("*-* All Finished *-*\n"); $finish; end