From 0e955d503e39c4c0039198de4351eed2833c94ef Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 2 Feb 2023 08:36:11 +0100 Subject: [PATCH] Handle references of static members of type aliases of a parametrized class (#3922) Signed-off-by: Ryszard Rozak --- src/V3LinkDot.cpp | 71 +++++++++++++++++----------------- src/V3Param.cpp | 37 ++++++++++++++++++ test_regress/t/t_class_param.v | 14 +++++++ 3 files changed, 87 insertions(+), 35 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 13825a9f9..40f1cbf5e 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2028,7 +2028,8 @@ private: DotPosition m_dotPos; // Scope part of dotted resolution VSymEnt* m_dotSymp; // SymEnt for dotted AstParse lookup const AstDot* m_dotp; // Current dot - bool m_unresolved; // Unresolved, needs help from V3Param + bool m_unresolvedCell; // Unresolved cell, needs help from V3Param + bool m_unresolvedClass; // Unresolved class reference, needs help from V3Param AstNode* m_unlinkedScopep; // Unresolved scope, needs corresponding VarXRef bool m_dotErr; // Error found in dotted resolution, ignore upwards string m_dotText; // String of dotted names found in below parseref @@ -2040,7 +2041,8 @@ private: m_dotp = nullptr; m_dotErr = false; m_dotText = ""; - m_unresolved = false; + m_unresolvedCell = false; + m_unresolvedClass = false; m_unlinkedScopep = nullptr; } string ascii() const { @@ -2049,7 +2051,8 @@ private: sstr << "ds=" << names[m_dotPos]; sstr << " dse" << cvtToHex(m_dotSymp); sstr << " txt=" << m_dotText; - sstr << " unr=" << m_unresolved; + sstr << " unrCell=" << m_unresolvedCell; + sstr << " unrClass=" << m_unresolvedClass; return sstr.str(); } } m_ds; // State to preserve across recursions @@ -2350,7 +2353,6 @@ private: // Dot(Dot(Dot(ParseRef(text), ... if (nodep->user3SetOnce()) return; UINFO(8, " " << nodep << endl); - bool replaceWithRhs = true; const DotStates lastStates = m_ds; const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed { @@ -2402,10 +2404,9 @@ private: } if (m_statep->forPrimary() && isParamedClassRef(nodep->lhsp())) { // Dots of paramed classes will be linked after deparameterization - m_ds.m_unresolved = true; - replaceWithRhs = false; + m_ds.m_unresolvedClass = true; } - if (m_ds.m_unresolved + if (m_ds.m_unresolvedCell && (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) { m_ds.m_unlinkedScopep = nodep->lhsp(); } @@ -2416,8 +2417,8 @@ private: iterateAndNextNull(nodep->rhsp()); // if (debug() >= 9) nodep->dumpTree("- dot-rho: "); } - if (start) { - if (replaceWithRhs) { + if (!m_ds.m_unresolvedClass) { + if (start) { AstNode* newp; if (m_ds.m_dotErr) { newp = new AstConst{nodep->fileline(), AstConst::BitFalse{}}; @@ -2427,23 +2428,25 @@ private: if (debug() >= 9) newp->dumpTree("- dot-out: "); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); + } else { // Dot midpoint + AstNodeExpr* newp = nodep->rhsp()->unlinkFrBack(); + if (m_ds.m_unresolvedCell) { + AstCellRef* const crp = new AstCellRef{ + nodep->fileline(), nodep->name(), nodep->lhsp()->unlinkFrBack(), newp}; + newp = crp; + } + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); } - } else { // Dot midpoint - AstNodeExpr* newp = nodep->rhsp()->unlinkFrBack(); - if (m_ds.m_unresolved) { - AstCellRef* const crp = new AstCellRef{nodep->fileline(), nodep->name(), - nodep->lhsp()->unlinkFrBack(), newp}; - newp = crp; - } - nodep->replaceWith(newp); - VL_DO_DANGLING(pushDeletep(nodep), nodep); } } + const bool unresolvedClass = m_ds.m_unresolvedClass; if (start) { m_ds = lastStates; } else { m_ds.m_dotp = lastStates.m_dotp; } + m_ds.m_unresolvedClass |= unresolvedClass; } void visit(AstSenItem* nodep) override { VL_RESTORER(m_inSens); @@ -2453,7 +2456,7 @@ private: void visit(AstParseRef* nodep) override { if (nodep->user3SetOnce()) return; UINFO(9, " linkPARSEREF " << m_ds.ascii() << " n=" << nodep << endl); - if (m_ds.m_unresolved && !m_ds.m_unlinkedScopep) return; + if (m_ds.m_unresolvedClass) return; // m_curSymp is symbol table of outer expression // m_ds.m_dotSymp is symbol table relative to "."'s above now UASSERT_OBJ(m_ds.m_dotSymp, nodep, "nullptr lookup symbol table"); @@ -2629,7 +2632,7 @@ private: varp->attrSplitVar(false); } m_ds.m_dotText = ""; - if (m_ds.m_unresolved && m_ds.m_unlinkedScopep) { + if (m_ds.m_unresolvedCell && m_ds.m_unlinkedScopep) { const string dotted = refp->dotted(); const size_t pos = dotted.find("__BRA__??__KET__"); // Arrays of interfaces all have the same parameters @@ -2641,7 +2644,7 @@ private: newp = new AstUnlinkedRef{nodep->fileline(), refp, refp->name(), m_ds.m_unlinkedScopep->unlinkFrBack()}; m_ds.m_unlinkedScopep = nullptr; - m_ds.m_unresolved = false; + m_ds.m_unresolvedCell = false; } } else { newp = refp; @@ -2951,19 +2954,17 @@ private: iterateChildren(nodep); } - if (m_ds.m_unresolved) { + if (m_ds.m_unresolvedClass) { // Unable to link before V3Param - if (m_ds.m_dotPos == DP_FINAL && m_ds.m_unlinkedScopep) { - AstNodeFTaskRef* const newftaskp = nodep->cloneTree(false); - newftaskp->dotted(m_ds.m_dotText); - AstNode* const newp - = new AstUnlinkedRef{nodep->fileline(), newftaskp, nodep->name(), - m_ds.m_unlinkedScopep->unlinkFrBack()}; - m_ds.m_unlinkedScopep = nullptr; - m_ds.m_unresolved = false; - nodep->replaceWith(newp); - } - // else: AstDot wasn't replaced and it will be linked after V3Param + return; + } else if (m_ds.m_unresolvedCell && m_ds.m_dotPos == DP_FINAL && m_ds.m_unlinkedScopep) { + AstNodeFTaskRef* const newftaskp = nodep->cloneTree(false); + newftaskp->dotted(m_ds.m_dotText); + AstNode* const newp = new AstUnlinkedRef{nodep->fileline(), newftaskp, nodep->name(), + m_ds.m_unlinkedScopep->unlinkFrBack()}; + m_ds.m_unlinkedScopep = nullptr; + m_ds.m_unresolvedCell = false; + nodep->replaceWith(newp); return; } else if (m_ds.m_dotp && m_ds.m_dotPos == DP_PACKAGE) { UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(), @@ -3118,7 +3119,7 @@ private: == DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart} UINFO(9, " deferring until after a V3Param pass: " << nodep << endl); m_ds.m_dotText += "__BRA__??__KET__"; - m_ds.m_unresolved = true; + m_ds.m_unresolvedCell = true; // And pass up m_ds.m_dotText } // Pass dot state down to fromp() @@ -3131,7 +3132,7 @@ private: iterateAndNextNull(nodep->attrp()); } } - if (m_ds.m_unresolved && m_ds.m_dotPos == DP_SCOPE) { + if (m_ds.m_unresolvedCell && m_ds.m_dotPos == DP_SCOPE) { AstNodeExpr* const exprp = nodep->bitp()->unlinkFrBack(); AstCellArrayRef* const newp = new AstCellArrayRef{nodep->fileline(), nodep->fromp()->name(), exprp}; diff --git a/src/V3Param.cpp b/src/V3Param.cpp index f5abe2e29..c4a8f69de 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -911,6 +911,7 @@ class ParamVisitor final : public VNVisitor { bool m_iterateModule = false; // Iterating module body string m_generateHierName; // Generate portion of hierarchy name string m_unlinkedTxt; // Text for AstUnlinkedRef + std::vector m_dots; // Dot references to process std::multimap m_cellps; // Cells left to process (in current module) std::multimap m_workQueue; // Modules left to process @@ -1007,6 +1008,21 @@ class ParamVisitor final : public VNVisitor { m_cellps.emplace(!isIface, nodep); } + // RHSs of AstDots need a relink when LHS is a parametrized class reference + void relinkDots() { + for (AstDot* const dotp : m_dots) { + const AstClassOrPackageRef* const classRefp = VN_AS(dotp->lhsp(), ClassOrPackageRef); + const AstClass* const lhsClassp = VN_AS(classRefp->classOrPackageNodep(), Class); + AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); + for (auto* itemp = lhsClassp->membersp(); itemp; itemp = itemp->nextp()) { + if (itemp->name() == rhsp->name()) { + rhsp->classOrPackageNodep(itemp); + break; + } + } + } + } + // VISITORS void visit(AstNodeModule* nodep) override { if (nodep->recursiveClone()) nodep->dead(true); // Fake, made for recursive elimination @@ -1113,6 +1129,25 @@ class ParamVisitor final : public VNVisitor { nodep->varp(nullptr); // Needs relink, as may remove pointed-to var } + void visit(AstDot* nodep) override { + iterate(nodep->lhsp()); + // Check if it is a reference to a field of a parameterized class. + // If so, the RHS should be updated, when the LHS is replaced + // by a class with actual parameter values. + const AstClass* lhsClassp = nullptr; + const AstClassOrPackageRef* const classRefp = VN_CAST(nodep->lhsp(), ClassOrPackageRef); + if (classRefp) lhsClassp = VN_CAST(classRefp->classOrPackageNodep(), Class); + AstNode* rhsDefp = nullptr; + AstClassOrPackageRef* const rhsp = VN_CAST(nodep->rhsp(), ClassOrPackageRef); + if (rhsp) rhsDefp = rhsp->classOrPackageNodep(); + if (lhsClassp && rhsDefp) { + m_dots.push_back(nodep); + // No need to iterate into rhsp, because there should be nothing to do + } else { + iterate(nodep->rhsp()); + } + } + void visit(AstUnlinkedRef* nodep) override { AstVarXRef* const varxrefp = VN_CAST(nodep->refp(), VarXRef); AstNodeFTaskRef* const taskrefp = VN_CAST(nodep->refp(), NodeFTaskRef); @@ -1273,6 +1308,8 @@ public: // Relies on modules already being in top-down-order iterate(netlistp); + relinkDots(); + // Re-sort module list to be in topological order and fix-up incorrect levels. We need to // do this globally at the end due to the presence of recursive modules, which might be // expanded in orders that reuse earlier specializations later at a lower level. diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index 17fb452fb..bcb9fa22c 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -70,6 +70,17 @@ class IntQueue; endfunction endclass +class ClsStatic; + static int x = 1; + static function int get_2; + return 2; + endfunction +endclass + +class ClsParam #(type T); + typedef T param_t; +endclass + module t (/*AUTOARG*/); Cls c12; @@ -139,6 +150,9 @@ module t (/*AUTOARG*/); if(Sum#(int)::sum != 16) $stop; if(Sum#(real)::sum != 0) $stop; + if (ClsParam#(ClsStatic)::param_t::x != 1) $stop; + if (ClsParam#(ClsStatic)::param_t::get_2() != 2) $stop; + $write("*-* All Finished *-*\n"); $finish; end