From 9d38e632693b75507ea496b3de1f991afaa84185 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 30 Jul 2025 09:14:13 -0400 Subject: [PATCH] Remove error on missing forward declarations of typedef in class (#6207). --- src/V3AstNodeOther.h | 6 +++++- src/V3AstNodes.cpp | 2 ++ src/V3LinkDot.cpp | 4 +++- test_regress/t/t_dump_json.out | 2 +- test_regress/t/t_json_only_tag.out | 2 +- test_regress/t/t_typedef_fwd_class.py | 18 ++++++++++++++++++ test_regress/t/t_typedef_fwd_class.v | 25 +++++++++++++++++++++++++ 7 files changed, 55 insertions(+), 4 deletions(-) create mode 100755 test_regress/t/t_typedef_fwd_class.py create mode 100644 test_regress/t/t_typedef_fwd_class.v diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 235ac2e5e..d4a603ed8 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1833,6 +1833,7 @@ class AstTypedef final : public AstNode { bool m_attrPublic = false; bool m_isHideLocal : 1; // Verilog local bool m_isHideProtected : 1; // Verilog protected + bool m_isUnderClass : 1; // Underneath class public: AstTypedef(FileLine* fl, const string& name, AstNode* attrsp, VFlagChildDType, @@ -1841,7 +1842,8 @@ public: , m_name{name} , m_declTokenNum{fl->tokenNum()} , m_isHideLocal{false} - , m_isHideProtected{false} { + , m_isHideProtected{false} + , m_isUnderClass{false} { childDTypep(dtp); // Only for parser addAttrsp(attrsp); dtypep(nullptr); // V3Width will resolve @@ -1868,6 +1870,8 @@ public: void isHideLocal(bool flag) { m_isHideLocal = flag; } bool isHideProtected() const { return m_isHideProtected; } void isHideProtected(bool flag) { m_isHideProtected = flag; } + bool isUnderClass() const { return m_isUnderClass; } + void isUnderClass(bool flag) { m_isUnderClass = flag; } void tag(const string& text) override { m_tag = text; } string tag() const override { return m_tag; } }; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index caf0d1331..d2184cab0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2169,6 +2169,7 @@ void AstTimeImport::dumpJson(std::ostream& str) const { void AstTypedef::dump(std::ostream& str) const { this->AstNode::dump(str); if (attrPublic()) str << " [PUBLIC]"; + if (isUnderClass()) str << " [UNDCLS]"; if (subDTypep()) { str << " -> "; subDTypep()->dump(str); @@ -2177,6 +2178,7 @@ void AstTypedef::dump(std::ostream& str) const { void AstTypedef::dumpJson(std::ostream& str) const { // dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file dumpJsonBoolFunc(str, attrPublic); + dumpJsonBoolFunc(str, isUnderClass); dumpJsonGen(str); } void AstTypedefFwd::dump(std::ostream& str) const { diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 585c9416c..59182ca99 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1564,6 +1564,7 @@ class LinkDotFindVisitor final : public VNVisitor { } void visit(AstTypedef* nodep) override { // FindVisitor:: UASSERT_OBJ(m_curSymp, nodep, "Typedef not under module/package/$unit"); + if (VN_IS(m_classOrPackagep, Class)) nodep->isUnderClass(true); iterateChildren(nodep); m_statep->insertSym(m_curSymp, nodep->name(), nodep, m_classOrPackagep); } @@ -4580,7 +4581,8 @@ class LinkDotResolveVisitor final : public VNVisitor { if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) { // Don't check if typedef is to a :: as might not be resolved // yet - if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); + if (!nodep->classOrPackagep() && !defp->isUnderClass()) + checkDeclOrder(nodep, defp); nodep->typedefp(defp); nodep->classOrPackagep(foundp->classOrPackagep()); } else if (AstParamTypeDType* const defp diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index 0e44b1848..7f456ecf7 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -865,7 +865,7 @@ ],"extendsp": []}, {"type":"CLASS","name":"process","addr":"(SN)","loc":"d,124:4,124:9","isExtended":false,"isInterfaceClass":false,"isVirtual":false,"origName":"process","level":5,"modPublic":false,"inLibrary":false,"dead":false,"recursiveClone":false,"recursive":false,"timeunit":"NONE","classOrPackagep":"UNLINKED","inlinesp": [], "stmtsp": [ - {"type":"TYPEDEF","name":"state","addr":"(TN)","loc":"d,131:9,131:14","dtypep":"UNLINKED","attrPublic":false, + {"type":"TYPEDEF","name":"state","addr":"(TN)","loc":"d,131:9,131:14","dtypep":"UNLINKED","attrPublic":false,"isUnderClass":false, "childDTypep": [ {"type":"DEFIMPLICITDTYPE","name":"__typeimpenum0","addr":"(UN)","loc":"d,125:15,125:19","dtypep":"UNLINKED","generic":false, "childDTypep": [ diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index 18f7938c2..96ab43284 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -5,7 +5,7 @@ {"type":"VAR","name":"clk_ip","addr":"(F)","loc":"d,14:11,14:17","dtypep":"(G)","origName":"clk_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"rst_ip","addr":"(H)","loc":"d,15:11,15:17","dtypep":"(G)","origName":"rst_ip","isSc":false,"isPrimaryIO":false,"direction":"INPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"foo_op","addr":"(I)","loc":"d,16:11,16:17","dtypep":"(G)","origName":"foo_op","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"childDTypep": [],"attrsp": []}, + {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"isUnderClass":false,"childDTypep": [],"attrsp": []}, {"type":"CELL","name":"itop","addr":"(L)","loc":"d,29:8,29:12","origName":"itop","recursive":false,"modp":"(M)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, {"type":"VAR","name":"itop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"this_struct","addr":"(P)","loc":"d,31:14,31:25","dtypep":"(Q)","origName":"this_struct","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, diff --git a/test_regress/t/t_typedef_fwd_class.py b/test_regress/t/t_typedef_fwd_class.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_typedef_fwd_class.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_typedef_fwd_class.v b/test_regress/t/t_typedef_fwd_class.v new file mode 100644 index 000000000..67ae2e046 --- /dev/null +++ b/test_regress/t/t_typedef_fwd_class.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 Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +typedef logic [3:0] T; + +class Cls; + extern static function int f(T x); + // This is after the usage above, but to match other simulators, + // no error about use after declaration + typedef logic [7:0] T; +endclass +function int Cls::f(T x); + return $bits(x); +endfunction + +module t; + initial begin + if (Cls::f('1) != 8) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule