diff --git a/Changes b/Changes index 98e3cd8a8..e7f6f383a 100644 --- a/Changes +++ b/Changes @@ -17,6 +17,7 @@ Verilator 5.039 devel * Add NOEFFECT warning, replacing previous `foreach` error. * Add SPECIFYIGN warning for specify constructs that were previously silently ignored. * Add enum base data type, and wire data type checking per IEEE. +* Add error on missing forward declarations (#6207). [Alex Solomatnikov] * Support member-level triggers for virtual interfaces (#5166) (#6148). [Yilou Wang] * Support disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.] * Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.] diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 831ed01c7..b0d8f7cfe 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -58,6 +58,27 @@ const VNTypeInfo VNType::typeInfoTable[] = { std::ostream& operator<<(std::ostream& os, VNType rhs); +//###################################################################### +// VFwdType + +bool VFwdType::isNodeCompatible(const AstNode* nodep) const { + const AstNode* defp = nodep; + if (const AstTypedef* const adefp = VN_CAST(defp, Typedef)) defp = adefp->subDTypep(); + if (const AstNodeDType* const adefp = VN_CAST(defp, NodeDType)) + defp = adefp->skipRefToNonRefp(); + switch (m_e) { + case VFwdType::NONE: return true; break; + case VFwdType::ENUM: return VN_IS(defp, EnumDType); break; + case VFwdType::STRUCT: return VN_IS(defp, StructDType); break; + case VFwdType::UNION: return VN_IS(defp, UnionDType); break; + case VFwdType::INTERFACE_CLASS: // FALLTHRU // TODO: Over permissive for now + case VFwdType::CLASS: return VN_IS(defp, ClassRefDType) || VN_IS(defp, Class); break; + default: v3fatalSrc("Bad case"); + } + VL_UNREACHABLE; + return false; // LCOV_EXCL_LINE +} + //###################################################################### // VSelfPointerText diff --git a/src/V3Ast.h b/src/V3Ast.h index cb19127d5..13790a5cd 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -304,6 +304,8 @@ public: explicit VFwdType(int _e) : m_e(static_cast(_e)) {} // Need () or GCC 4.8 false warning constexpr operator en() const { return m_e; } + // Is a node type compatible with the declaration + bool isNodeCompatible(const AstNode* nodep) const; }; constexpr bool operator==(const VFwdType& lhs, const VFwdType& rhs) { return lhs.m_e == rhs.m_e; } constexpr bool operator==(const VFwdType& lhs, VFwdType::en rhs) { return lhs.m_e == rhs; } @@ -2236,6 +2238,8 @@ public: } virtual void tag(const string& text) {} virtual string tag() const { return ""; } + virtual uint32_t declTokenNum() const { return 0; } + virtual void declTokenNumSetMin(uint32_t tokenNum) {} virtual string verilogKwd() const { return ""; } string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied string origNameProtect() const; // origName with --protect-id applied diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index b819ee829..f8d12c686 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1829,6 +1829,7 @@ class AstTypedef final : public AstNode { string m_name; string m_tag; // Holds the string of the verilator tag -- used in XML output. + uint32_t m_declTokenNum; // Declaration token number bool m_attrPublic = false; bool m_isHideLocal : 1; // Verilog local bool m_isHideProtected : 1; // Verilog protected @@ -1838,6 +1839,7 @@ public: AstNodeDType* dtp) : ASTGEN_SUPER_Typedef(fl) , m_name{name} + , m_declTokenNum{fl->tokenNum()} , m_isHideLocal{false} , m_isHideProtected{false} { childDTypep(dtp); // Only for parser @@ -1852,6 +1854,10 @@ public: return dtypep() ? dtypep() : childDTypep(); } // METHODS + uint32_t declTokenNum() const override { return m_declTokenNum; } + void declTokenNumSetMin(uint32_t tokenNum) override { + m_declTokenNum = std::min(m_declTokenNum, tokenNum); + } string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override VL_MT_SAFE { return true; } bool hasDType() const override VL_MT_SAFE { return true; } @@ -1868,15 +1874,20 @@ public: class AstTypedefFwd final : public AstNode { // Forward declaration of a type; stripped after netlist parsing is complete string m_name; + const VFwdType m_fwdType; // Forward type for lint check public: - AstTypedefFwd(FileLine* fl, const string& name) + AstTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) : ASTGEN_SUPER_TypedefFwd(fl) - , m_name{name} {} + , m_name{name} + , m_fwdType{fwdType} {} ASTGEN_MEMBERS_AstTypedefFwd; // METHODS string name() const override VL_MT_STABLE { return m_name; } + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; bool maybePointedTo() const override VL_MT_SAFE { return true; } + VFwdType fwdType() const { return m_fwdType; } }; class AstUdpTable final : public AstNode { // @astgen op1 := linesp : List[AstUdpTableLine] @@ -2505,6 +2516,7 @@ class AstClass final : public AstNodeModule { // @astgen op4 := extendsp : List[AstClassExtends] // MEMBERS // @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with + uint32_t m_declTokenNum; // Declaration token number VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends) bool m_extended = false; // Is extension or extended by other classes bool m_interfaceClass = false; // Interface class @@ -2514,7 +2526,8 @@ class AstClass final : public AstNodeModule { public: AstClass(FileLine* fl, const string& name, const string& libname) - : ASTGEN_SUPER_Class(fl, name, libname) {} + : ASTGEN_SUPER_Class(fl, name, libname) + , m_declTokenNum{fl->tokenNum()} {} ASTGEN_MEMBERS_AstClass; string verilogKwd() const override { return "class"; } bool maybePointedTo() const override VL_MT_SAFE { return true; } @@ -2538,6 +2551,10 @@ public: // Return true if this class is an extension of base class (SLOW) // Accepts nullptrs static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp); + uint32_t declTokenNum() const override { return m_declTokenNum; } + void declTokenNumSetMin(uint32_t tokenNum) override { + m_declTokenNum = std::min(m_declTokenNum, tokenNum); + } void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; } VBaseOverride baseOverride() const { return m_baseOverride; } // Return the lowest class extended from, or this class diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index c4347bbfe..b02358d5d 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1771,6 +1771,7 @@ void AstClass::dump(std::ostream& str) const { if (useVirtualPublic()) str << " [VIRPUB]"; } void AstClass::dumpJson(std::ostream& str) const { + // dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file dumpJsonBoolFunc(str, isExtended); dumpJsonBoolFunc(str, isInterfaceClass); dumpJsonBoolFunc(str, isVirtual); @@ -2161,9 +2162,18 @@ 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); dumpJsonGen(str); } +void AstTypedefFwd::dump(std::ostream& str) const { + this->AstNode::dump(str); + str << " [" << fwdType().ascii() << "]"; +} +void AstTypedefFwd::dumpJson(std::ostream& str) const { + dumpJsonStr(str, "fwdType", fwdType().ascii()); + dumpJsonGen(str); +} void AstNodeRange::dump(std::ostream& str) const { this->AstNode::dump(str); } void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); } void AstRange::dump(std::ostream& str) const { diff --git a/src/V3FileLine.cpp b/src/V3FileLine.cpp index b7778d2de..d65db10da 100644 --- a/src/V3FileLine.cpp +++ b/src/V3FileLine.cpp @@ -293,6 +293,8 @@ void FileLine::lineDirectiveParse(const char* textp, string& filenameRef, int& l } void FileLine::forwardToken(const char* textp, size_t size, bool trackLines) { + static int s_tokenNum = 1; + m_tokenNum = s_tokenNum++; for (const char* sp = textp; size && *sp; ++sp, --size) { if (*sp == '\n') { if (trackLines) linenoInc(); diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 430ad04fd..27ad85ef7 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -146,7 +146,7 @@ class FileLine final { bool m_waive : 1; // Waive warning - pack next to the line number to save 8 bytes of storage unsigned m_contentLineno : 31; // Line number within source stream // 64-bit word # 1 - uint32_t m_pad = 0; // space for project coming soon + uint32_t m_tokenNum = 0; // Token number in processing order int m_firstLineno = 0; // `line corrected token's first line number // 64-bit word # 2 uint32_t m_firstColumn : 24; // `line corrected token's first column number @@ -197,6 +197,7 @@ public: , m_filenameno{from.m_filenameno} , m_waive{from.m_waive} , m_contentLineno{from.m_contentLineno} + , m_tokenNum{from.m_tokenNum} , m_firstLineno{from.m_firstLineno} , m_firstColumn{from.m_firstColumn} , m_lastColumn{from.m_lastColumn} @@ -210,6 +211,7 @@ public: , m_filenameno{fromp->m_filenameno} , m_waive{fromp->m_waive} , m_contentLineno{fromp->m_contentLineno} + , m_tokenNum{fromp->m_tokenNum} , m_firstLineno{fromp->m_firstLineno} , m_firstColumn{fromp->m_firstColumn} , m_lastColumn{fromp->m_lastColumn} @@ -272,6 +274,7 @@ public: } // Advance last line/column based on given text void forwardToken(const char* textp, size_t size, bool trackLines = true); + uint32_t tokenNum() const VL_MT_SAFE { return m_tokenNum; } int firstLineno() const VL_MT_SAFE { return m_firstLineno; } int firstColumn() const VL_MT_SAFE { return static_cast(m_firstColumn); } int lastLineno() const VL_MT_SAFE { return m_firstLineno + m_lastLinenoAdder; } @@ -386,9 +389,10 @@ public: /// Simplified information vs warnContextPrimary() to make dump clearer string warnContextSecondary() const { return warnContext(); } bool operator==(const FileLine& rhs) const { - return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn - && m_lastLinenoAdder == rhs.m_lastLinenoAdder && m_lastColumn == rhs.m_lastColumn - && m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx); + return (m_tokenNum == rhs.m_tokenNum && m_firstLineno == rhs.m_firstLineno + && m_firstColumn == rhs.m_firstColumn && m_lastLinenoAdder == rhs.m_lastLinenoAdder + && m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno + && m_msgEnIdx == rhs.m_msgEnIdx); } bool equalFirstLineCol(const FileLine& rhs) const { return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno @@ -408,6 +412,8 @@ public: for (size_t i = 0; i < msgEn().size(); ++i) { if (msgEn().test(i) != rhs.msgEn().test(i)) return rhs.msgEn().test(i) ? -1 : 1; } + // TokenNum is compared last as makes more logical sort order by file/line first + if (m_tokenNum != rhs.m_tokenNum) return (m_tokenNum < rhs.m_tokenNum) ? -1 : 1; return 0; // (*this) and rhs are equivalent } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 425bb1f40..585c9416c 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -310,6 +310,8 @@ public: << nodep->warnContextPrimary() << '\n' << fnodep->warnOther() << "... Location of original declaration\n" << fnodep->warnContextSecondary()); + nodep->declTokenNumSetMin(0); // Disable checking forward typedefs + fnodep->declTokenNumSetMin(0); // Disable checking forward typedefs } else { nodep->v3error("Unsupported in C: " << ucfirst(nodeTextType(nodep)) << " has the same name as " @@ -2063,8 +2065,13 @@ class LinkDotParamVisitor final : public VNVisitor { } void visit(AstTypedefFwd* nodep) override { // ParamVisitor:: VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name()); - if (!foundp && v3Global.opt.pedantic() - && nodep->name() != "process") { // Process is dangling as isn't implemented yet + if (foundp) { + // If the typedef was earlier in source order (tokenNum), then remember that earlier + // point to avoid error something wasn't declared + // Might be forward declaring something odd (with declTokenNumSetMin not implemented, + // but should be harmless to ignore as this is just for error detection + foundp->nodep()->declTokenNumSetMin(nodep->fileline()->tokenNum()); + } else if (v3Global.opt.pedantic()) { // We only check it was ever really defined in pedantic mode, as it // might have been in a header file referring to a module we never // needed so there are false positives @@ -2645,6 +2652,23 @@ class LinkDotResolveVisitor final : public VNVisitor { symIterateNull(nodep, m_statep->getNodeSym(nodep)); } + void checkDeclOrder(AstNode* nodep, AstNode* declp) { + uint32_t declTokenNum = declp->declTokenNum(); + if (!declTokenNum) return; // Not implemented/valid on this object + if (nodep->fileline()->tokenNum() < declTokenNum) { + UINFO(1, "Related node " << nodep->fileline()->tokenNum() << " " << nodep); + UINFO(1, "Related decl " << declTokenNum << " " << declp); + nodep->v3error("Reference to " + << nodep->prettyNameQ() << " before declaration (IEEE 1800-2023 6.18)\n" + << nodep->warnMore() + << "... Suggest move the declaration before the reference, or use a " + "forward typedef\n" + << nodep->warnContextPrimary() << '\n' + << declp->warnOther() << "... Location of original declaration\n" + << declp->warnContextSecondary()); + } + } + void replaceWithCheckBreak(AstNode* oldp, AstNodeDType* newp) { // Flag now to avoid V3Broken throwing an internal error if (oldp->wouldBreak(newp)) { @@ -3505,6 +3529,9 @@ class LinkDotResolveVisitor final : public VNVisitor { ok = m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE; if (ok) { AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()}; + // Don't check if typedef is to a :: as might not be + // resolved yet + if (m_ds.m_dotPos == DP_NONE) checkDeclOrder(nodep, defp); refp->typedefp(defp); if (VN_IS(nodep->backp(), SelExtract)) { m_packedArrayDtp = refp; @@ -4551,6 +4578,9 @@ class LinkDotResolveVisitor final : public VNVisitor { foundp = m_curSymp->findIdFlat(nodep->name()); } 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); nodep->typedefp(defp); nodep->classOrPackagep(foundp->classOrPackagep()); } else if (AstParamTypeDType* const defp @@ -4566,6 +4596,9 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->classOrPackagep(foundp->classOrPackagep()); } } else if (AstClass* const defp = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) { + // Don't check if typedef is to a :: as might not be resolved + // yet + if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp); AstPin* const paramsp = nodep->paramsp(); if (paramsp) paramsp->unlinkFrBackWithNext(); AstClassRefDType* const newp diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 09b527a65..4eba561cc 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -782,19 +782,7 @@ class ParamProcessor final { // Constify may have caused pinp->exprp to change rawTypep = VN_AS(pinp->exprp(), NodeDType); exprp = rawTypep->skipRefToNonRefp(); - bool ok = true; - switch (modvarp->fwdType()) { - case VFwdType::NONE: ok = true; break; - case VFwdType::ENUM: ok = VN_IS(exprp, EnumDType); break; - case VFwdType::STRUCT: ok = VN_IS(exprp, StructDType); break; - case VFwdType::UNION: ok = VN_IS(exprp, UnionDType); break; - case VFwdType::CLASS: ok = VN_IS(exprp, ClassRefDType); break; - case VFwdType::INTERFACE_CLASS: // TODO: Over permissive for now: - ok = VN_IS(exprp, ClassRefDType); - break; - default: modvarp->v3fatalSrc("Bad case"); - } - if (!ok) { + if (!modvarp->fwdType().isNodeCompatible(exprp)) { pinp->v3error("Parameter type expression type " << exprp->prettyDTypeNameQ() << " violates parameter's forwarding type '" diff --git a/src/V3ParseGrammar.cpp b/src/V3ParseGrammar.cpp index 42d54bc0a..08b0fd4b4 100644 --- a/src/V3ParseGrammar.cpp +++ b/src/V3ParseGrammar.cpp @@ -260,7 +260,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name, } if (VN_IS(dtypep, ParseTypeDType)) { // Parser needs to know what is a type - AstNode* const newp = new AstTypedefFwd{fileline, name}; + AstNode* const newp = new AstTypedefFwd{fileline, name, VFwdType::NONE}; AstNode::addNext(nodep, newp); } // Don't set dtypep in the ranging; diff --git a/src/verilog.y b/src/verilog.y index 44354aece..680c0bd93 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -183,8 +183,8 @@ public: PARSEP->tagNodep(nodep); return nodep; } - AstNode* createTypedefFwd(FileLine* fl, const string& name) { - AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name}; + AstNode* createTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) { + AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name, fwdType}; PARSEP->tagNodep(nodep); return nodep; } @@ -2695,15 +2695,15 @@ type_declaration: // ==IEEE: type_declaration | yTYPEDEF idAny/*interface*/ '.' idAny/*type*/ idAny/*type*/ dtypeAttrListE ';' { $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 typedef in this context"); } // // idAny as also allows redeclaring same typedef again - | yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($2, *$2); } + | yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($2, *$2, VFwdType::NONE); } // // IEEE: expanded forward_type to prevent conflict - | yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } - | yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($4, *$4); } + | yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::ENUM); } + | yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::STRUCT); } + | yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::UNION); } + | yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::CLASS); } + | yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($4, *$4, VFwdType::INTERFACE_CLASS); } // - | yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3); } + | yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($3, *$3, VFwdType::NONE); } ; dtypeAttrListE: diff --git a/test_regress/t/t_dump_json.out b/test_regress/t/t_dump_json.out index c2dc38ecd..0e44b1848 100644 --- a/test_regress/t/t_dump_json.out +++ b/test_regress/t/t_dump_json.out @@ -534,7 +534,7 @@ "childDTypep": [ {"type":"PARSETYPEDTYPE","name":"","addr":"(PI)","loc":"d,32:20,32:24","dtypep":"UNLINKED","generic":false} ],"delayp": [],"valuep": [],"attrsp": []}, - {"type":"TYPEDEFFWD","name":"T","addr":"(QI)","loc":"d,32:25,32:26"}, + {"type":"TYPEDEFFWD","name":"T","addr":"(QI)","loc":"d,32:25,32:26","fwdType":"none"}, {"type":"VAR","name":"m_bound","addr":"(RI)","loc":"d,33:21,33:28","dtypep":"UNLINKED","origName":"m_bound","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":"NONE","varType":"VAR","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"ignorePostWrite":false,"ignoreSchedWrite":false,"sensIfacep":"UNLINKED", "childDTypep": [ {"type":"BASICDTYPE","name":"int","addr":"(SI)","loc":"d,33:17,33:20","dtypep":"(SI)","keyword":"int","range":"31:0","generic":false,"rangep": []} diff --git a/test_regress/t/t_typedef_fwd.py b/test_regress/t/t_typedef_fwd.py new file mode 100755 index 000000000..cca4c9e73 --- /dev/null +++ b/test_regress/t/t_typedef_fwd.py @@ -0,0 +1,16 @@ +#!/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('linter') + +test.lint() + +test.passes() diff --git a/test_regress/t/t_typedef_fwd.v b/test_regress/t/t_typedef_fwd.v new file mode 100644 index 000000000..c7d16e283 --- /dev/null +++ b/test_regress/t/t_typedef_fwd.v @@ -0,0 +1,42 @@ +// 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 + +package P; +`ifndef TEST_NO_TYPEDEFS + typedef enum enum_t; + typedef struct struct_t; + typedef union union_t; + typedef class ClsB; + typedef interface class IfC; + typedef generic_t; +`endif + + class ClsA; + enum_t m_e; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + struct_t m_s; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + union_t m_u; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + ClsB m_b; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + IfC m_i; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + generic_t m_g; // <--- Error need forward decl (if TEST_NO_TYPEDEFS) + endclass + + typedef enum {N = 0} enum_t; + + typedef struct packed {int s;} struct_t; + typedef union packed {int s;} union_t; + + class ClsB; + endclass + + interface class IfC; + endclass + + typedef int generic_t; + +endpackage + +module t; +endmodule diff --git a/test_regress/t/t_typedef_fwd_bad.out b/test_regress/t/t_typedef_fwd_bad.out new file mode 100644 index 000000000..497f19e7a --- /dev/null +++ b/test_regress/t/t_typedef_fwd_bad.out @@ -0,0 +1,44 @@ +%Error: t/t_typedef_fwd.v:18:5: Reference to 'enum_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 18 | enum_t m_e; + | ^~~~~~ + t/t_typedef_fwd.v:26:24: ... Location of original declaration + 26 | typedef enum {N = 0} enum_t; + | ^~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_typedef_fwd.v:19:5: Reference to 'struct_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 19 | struct_t m_s; + | ^~~~~~~~ + t/t_typedef_fwd.v:28:34: ... Location of original declaration + 28 | typedef struct packed {int s;} struct_t; + | ^~~~~~~~ +%Error: t/t_typedef_fwd.v:20:5: Reference to 'union_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 20 | union_t m_u; + | ^~~~~~~ + t/t_typedef_fwd.v:29:33: ... Location of original declaration + 29 | typedef union packed {int s;} union_t; + | ^~~~~~~ +%Error: t/t_typedef_fwd.v:21:5: Reference to 'ClsB' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 21 | ClsB m_b; + | ^~~~ + t/t_typedef_fwd.v:31:3: ... Location of original declaration + 31 | class ClsB; + | ^~~~~ +%Error: t/t_typedef_fwd.v:22:5: Reference to 'IfC' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 22 | IfC m_i; + | ^~~ + t/t_typedef_fwd.v:34:13: ... Location of original declaration + 34 | interface class IfC; + | ^~~~~ +%Error: t/t_typedef_fwd.v:23:5: Reference to 'generic_t' before declaration (IEEE 1800-2023 6.18) + : ... Suggest move the declaration before the reference, or use a forward typedef + 23 | generic_t m_g; + | ^~~~~~~~~ + t/t_typedef_fwd.v:37:15: ... Location of original declaration + 37 | typedef int generic_t; + | ^~~~~~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_typedef_fwd_bad.py b/test_regress/t/t_typedef_fwd_bad.py new file mode 100755 index 000000000..cc0ca5d61 --- /dev/null +++ b/test_regress/t/t_typedef_fwd_bad.py @@ -0,0 +1,17 @@ +#!/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('linter') +test.top_filename = 't/t_typedef_fwd.v' + +test.lint(v_flags2=['+define+TEST_NO_TYPEDEFS'], fails=True, expect_filename=test.golden_filename) + +test.passes()