Add error on missing forward declarations (#6207).
This commit is contained in:
parent
d8dbb08a95
commit
f3560837ec
1
Changes
1
Changes
|
|
@ -17,6 +17,7 @@ Verilator 5.039 devel
|
||||||
* Add NOEFFECT warning, replacing previous `foreach` error.
|
* Add NOEFFECT warning, replacing previous `foreach` error.
|
||||||
* Add SPECIFYIGN warning for specify constructs that were previously silently ignored.
|
* Add SPECIFYIGN warning for specify constructs that were previously silently ignored.
|
||||||
* Add enum base data type, and wire data type checking per IEEE.
|
* 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 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 disabling a fork in additional contexts (#5432 partial) (#6174) (#6183). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.]
|
* Support disable dotted references (#6154). [Ryszard Rozak, Antmicro Ltd.]
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,27 @@ const VNTypeInfo VNType::typeInfoTable[] = {
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& os, VNType rhs);
|
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
|
// VSelfPointerText
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -304,6 +304,8 @@ public:
|
||||||
explicit VFwdType(int _e)
|
explicit VFwdType(int _e)
|
||||||
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
: m_e(static_cast<en>(_e)) {} // Need () or GCC 4.8 false warning
|
||||||
constexpr operator en() const { return m_e; }
|
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, const VFwdType& rhs) { return lhs.m_e == rhs.m_e; }
|
||||||
constexpr bool operator==(const VFwdType& lhs, VFwdType::en rhs) { return lhs.m_e == rhs; }
|
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 void tag(const string& text) {}
|
||||||
virtual string tag() const { return ""; }
|
virtual string tag() const { return ""; }
|
||||||
|
virtual uint32_t declTokenNum() const { return 0; }
|
||||||
|
virtual void declTokenNumSetMin(uint32_t tokenNum) {}
|
||||||
virtual string verilogKwd() const { return ""; }
|
virtual string verilogKwd() const { return ""; }
|
||||||
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
|
string nameProtect() const VL_MT_STABLE; // Name with --protect-id applied
|
||||||
string origNameProtect() const; // origName with --protect-id applied
|
string origNameProtect() const; // origName with --protect-id applied
|
||||||
|
|
|
||||||
|
|
@ -1829,6 +1829,7 @@ class AstTypedef final : public AstNode {
|
||||||
|
|
||||||
string m_name;
|
string m_name;
|
||||||
string m_tag; // Holds the string of the verilator tag -- used in XML output.
|
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_attrPublic = false;
|
||||||
bool m_isHideLocal : 1; // Verilog local
|
bool m_isHideLocal : 1; // Verilog local
|
||||||
bool m_isHideProtected : 1; // Verilog protected
|
bool m_isHideProtected : 1; // Verilog protected
|
||||||
|
|
@ -1838,6 +1839,7 @@ public:
|
||||||
AstNodeDType* dtp)
|
AstNodeDType* dtp)
|
||||||
: ASTGEN_SUPER_Typedef(fl)
|
: ASTGEN_SUPER_Typedef(fl)
|
||||||
, m_name{name}
|
, m_name{name}
|
||||||
|
, m_declTokenNum{fl->tokenNum()}
|
||||||
, m_isHideLocal{false}
|
, m_isHideLocal{false}
|
||||||
, m_isHideProtected{false} {
|
, m_isHideProtected{false} {
|
||||||
childDTypep(dtp); // Only for parser
|
childDTypep(dtp); // Only for parser
|
||||||
|
|
@ -1852,6 +1854,10 @@ public:
|
||||||
return dtypep() ? dtypep() : childDTypep();
|
return dtypep() ? dtypep() : childDTypep();
|
||||||
}
|
}
|
||||||
// METHODS
|
// 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; }
|
string name() const override VL_MT_STABLE { return m_name; }
|
||||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
bool hasDType() 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 {
|
class AstTypedefFwd final : public AstNode {
|
||||||
// Forward declaration of a type; stripped after netlist parsing is complete
|
// Forward declaration of a type; stripped after netlist parsing is complete
|
||||||
string m_name;
|
string m_name;
|
||||||
|
const VFwdType m_fwdType; // Forward type for lint check
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstTypedefFwd(FileLine* fl, const string& name)
|
AstTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType)
|
||||||
: ASTGEN_SUPER_TypedefFwd(fl)
|
: ASTGEN_SUPER_TypedefFwd(fl)
|
||||||
, m_name{name} {}
|
, m_name{name}
|
||||||
|
, m_fwdType{fwdType} {}
|
||||||
ASTGEN_MEMBERS_AstTypedefFwd;
|
ASTGEN_MEMBERS_AstTypedefFwd;
|
||||||
// METHODS
|
// METHODS
|
||||||
string name() const override VL_MT_STABLE { return m_name; }
|
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; }
|
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
||||||
|
VFwdType fwdType() const { return m_fwdType; }
|
||||||
};
|
};
|
||||||
class AstUdpTable final : public AstNode {
|
class AstUdpTable final : public AstNode {
|
||||||
// @astgen op1 := linesp : List[AstUdpTableLine]
|
// @astgen op1 := linesp : List[AstUdpTableLine]
|
||||||
|
|
@ -2505,6 +2516,7 @@ class AstClass final : public AstNodeModule {
|
||||||
// @astgen op4 := extendsp : List[AstClassExtends]
|
// @astgen op4 := extendsp : List[AstClassExtends]
|
||||||
// MEMBERS
|
// MEMBERS
|
||||||
// @astgen ptr := m_classOrPackagep : Optional[AstClassPackage] // Package to be emitted with
|
// @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)
|
VBaseOverride m_baseOverride; // BaseOverride (inital/final/extends)
|
||||||
bool m_extended = false; // Is extension or extended by other classes
|
bool m_extended = false; // Is extension or extended by other classes
|
||||||
bool m_interfaceClass = false; // Interface class
|
bool m_interfaceClass = false; // Interface class
|
||||||
|
|
@ -2514,7 +2526,8 @@ class AstClass final : public AstNodeModule {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstClass(FileLine* fl, const string& name, const string& libname)
|
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;
|
ASTGEN_MEMBERS_AstClass;
|
||||||
string verilogKwd() const override { return "class"; }
|
string verilogKwd() const override { return "class"; }
|
||||||
bool maybePointedTo() const override VL_MT_SAFE { return true; }
|
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)
|
// Return true if this class is an extension of base class (SLOW)
|
||||||
// Accepts nullptrs
|
// Accepts nullptrs
|
||||||
static bool isClassExtendedFrom(const AstClass* refClassp, const AstClass* baseClassp);
|
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; }
|
void baseOverride(const VBaseOverride& flag) { m_baseOverride = flag; }
|
||||||
VBaseOverride baseOverride() const { return m_baseOverride; }
|
VBaseOverride baseOverride() const { return m_baseOverride; }
|
||||||
// Return the lowest class extended from, or this class
|
// Return the lowest class extended from, or this class
|
||||||
|
|
|
||||||
|
|
@ -1771,6 +1771,7 @@ void AstClass::dump(std::ostream& str) const {
|
||||||
if (useVirtualPublic()) str << " [VIRPUB]";
|
if (useVirtualPublic()) str << " [VIRPUB]";
|
||||||
}
|
}
|
||||||
void AstClass::dumpJson(std::ostream& str) const {
|
void AstClass::dumpJson(std::ostream& str) const {
|
||||||
|
// dumpJsonNumFunc(str, declTokenNum); // Not dumped as adding token changes whole file
|
||||||
dumpJsonBoolFunc(str, isExtended);
|
dumpJsonBoolFunc(str, isExtended);
|
||||||
dumpJsonBoolFunc(str, isInterfaceClass);
|
dumpJsonBoolFunc(str, isInterfaceClass);
|
||||||
dumpJsonBoolFunc(str, isVirtual);
|
dumpJsonBoolFunc(str, isVirtual);
|
||||||
|
|
@ -2161,9 +2162,18 @@ void AstTypedef::dump(std::ostream& str) const {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void AstTypedef::dumpJson(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, attrPublic);
|
||||||
dumpJsonGen(str);
|
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::dump(std::ostream& str) const { this->AstNode::dump(str); }
|
||||||
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
void AstNodeRange::dumpJson(std::ostream& str) const { dumpJsonGen(str); }
|
||||||
void AstRange::dump(std::ostream& str) const {
|
void AstRange::dump(std::ostream& str) const {
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
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) {
|
for (const char* sp = textp; size && *sp; ++sp, --size) {
|
||||||
if (*sp == '\n') {
|
if (*sp == '\n') {
|
||||||
if (trackLines) linenoInc();
|
if (trackLines) linenoInc();
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
unsigned m_contentLineno : 31; // Line number within source stream
|
||||||
// 64-bit word # 1
|
// 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
|
int m_firstLineno = 0; // `line corrected token's first line number
|
||||||
// 64-bit word # 2
|
// 64-bit word # 2
|
||||||
uint32_t m_firstColumn : 24; // `line corrected token's first column number
|
uint32_t m_firstColumn : 24; // `line corrected token's first column number
|
||||||
|
|
@ -197,6 +197,7 @@ public:
|
||||||
, m_filenameno{from.m_filenameno}
|
, m_filenameno{from.m_filenameno}
|
||||||
, m_waive{from.m_waive}
|
, m_waive{from.m_waive}
|
||||||
, m_contentLineno{from.m_contentLineno}
|
, m_contentLineno{from.m_contentLineno}
|
||||||
|
, m_tokenNum{from.m_tokenNum}
|
||||||
, m_firstLineno{from.m_firstLineno}
|
, m_firstLineno{from.m_firstLineno}
|
||||||
, m_firstColumn{from.m_firstColumn}
|
, m_firstColumn{from.m_firstColumn}
|
||||||
, m_lastColumn{from.m_lastColumn}
|
, m_lastColumn{from.m_lastColumn}
|
||||||
|
|
@ -210,6 +211,7 @@ public:
|
||||||
, m_filenameno{fromp->m_filenameno}
|
, m_filenameno{fromp->m_filenameno}
|
||||||
, m_waive{fromp->m_waive}
|
, m_waive{fromp->m_waive}
|
||||||
, m_contentLineno{fromp->m_contentLineno}
|
, m_contentLineno{fromp->m_contentLineno}
|
||||||
|
, m_tokenNum{fromp->m_tokenNum}
|
||||||
, m_firstLineno{fromp->m_firstLineno}
|
, m_firstLineno{fromp->m_firstLineno}
|
||||||
, m_firstColumn{fromp->m_firstColumn}
|
, m_firstColumn{fromp->m_firstColumn}
|
||||||
, m_lastColumn{fromp->m_lastColumn}
|
, m_lastColumn{fromp->m_lastColumn}
|
||||||
|
|
@ -272,6 +274,7 @@ public:
|
||||||
}
|
}
|
||||||
// Advance last line/column based on given text
|
// Advance last line/column based on given text
|
||||||
void forwardToken(const char* textp, size_t size, bool trackLines = true);
|
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 firstLineno() const VL_MT_SAFE { return m_firstLineno; }
|
||||||
int firstColumn() const VL_MT_SAFE { return static_cast<int>(m_firstColumn); }
|
int firstColumn() const VL_MT_SAFE { return static_cast<int>(m_firstColumn); }
|
||||||
int lastLineno() const VL_MT_SAFE { return m_firstLineno + m_lastLinenoAdder; }
|
int lastLineno() const VL_MT_SAFE { return m_firstLineno + m_lastLinenoAdder; }
|
||||||
|
|
@ -386,9 +389,10 @@ public:
|
||||||
/// Simplified information vs warnContextPrimary() to make dump clearer
|
/// Simplified information vs warnContextPrimary() to make dump clearer
|
||||||
string warnContextSecondary() const { return warnContext(); }
|
string warnContextSecondary() const { return warnContext(); }
|
||||||
bool operator==(const FileLine& rhs) const {
|
bool operator==(const FileLine& rhs) const {
|
||||||
return (m_firstLineno == rhs.m_firstLineno && m_firstColumn == rhs.m_firstColumn
|
return (m_tokenNum == rhs.m_tokenNum && m_firstLineno == rhs.m_firstLineno
|
||||||
&& m_lastLinenoAdder == rhs.m_lastLinenoAdder && m_lastColumn == rhs.m_lastColumn
|
&& m_firstColumn == rhs.m_firstColumn && m_lastLinenoAdder == rhs.m_lastLinenoAdder
|
||||||
&& m_filenameno == rhs.m_filenameno && m_msgEnIdx == rhs.m_msgEnIdx);
|
&& m_lastColumn == rhs.m_lastColumn && m_filenameno == rhs.m_filenameno
|
||||||
|
&& m_msgEnIdx == rhs.m_msgEnIdx);
|
||||||
}
|
}
|
||||||
bool equalFirstLineCol(const FileLine& rhs) const {
|
bool equalFirstLineCol(const FileLine& rhs) const {
|
||||||
return (m_filenameno == rhs.m_filenameno && m_firstLineno == rhs.m_firstLineno
|
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) {
|
for (size_t i = 0; i < msgEn().size(); ++i) {
|
||||||
if (msgEn().test(i) != rhs.msgEn().test(i)) return rhs.msgEn().test(i) ? -1 : 1;
|
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
|
return 0; // (*this) and rhs are equivalent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,8 @@ public:
|
||||||
<< nodep->warnContextPrimary() << '\n'
|
<< nodep->warnContextPrimary() << '\n'
|
||||||
<< fnodep->warnOther() << "... Location of original declaration\n"
|
<< fnodep->warnOther() << "... Location of original declaration\n"
|
||||||
<< fnodep->warnContextSecondary());
|
<< fnodep->warnContextSecondary());
|
||||||
|
nodep->declTokenNumSetMin(0); // Disable checking forward typedefs
|
||||||
|
fnodep->declTokenNumSetMin(0); // Disable checking forward typedefs
|
||||||
} else {
|
} else {
|
||||||
nodep->v3error("Unsupported in C: "
|
nodep->v3error("Unsupported in C: "
|
||||||
<< ucfirst(nodeTextType(nodep)) << " has the same name as "
|
<< ucfirst(nodeTextType(nodep)) << " has the same name as "
|
||||||
|
|
@ -2063,8 +2065,13 @@ class LinkDotParamVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
void visit(AstTypedefFwd* nodep) override { // ParamVisitor::
|
void visit(AstTypedefFwd* nodep) override { // ParamVisitor::
|
||||||
VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name());
|
VSymEnt* const foundp = m_statep->getNodeSym(nodep)->findIdFallback(nodep->name());
|
||||||
if (!foundp && v3Global.opt.pedantic()
|
if (foundp) {
|
||||||
&& nodep->name() != "process") { // Process is dangling as isn't implemented yet
|
// 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
|
// 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
|
// might have been in a header file referring to a module we never
|
||||||
// needed so there are false positives
|
// needed so there are false positives
|
||||||
|
|
@ -2645,6 +2652,23 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
symIterateNull(nodep, m_statep->getNodeSym(nodep));
|
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) {
|
void replaceWithCheckBreak(AstNode* oldp, AstNodeDType* newp) {
|
||||||
// Flag now to avoid V3Broken throwing an internal error
|
// Flag now to avoid V3Broken throwing an internal error
|
||||||
if (oldp->wouldBreak(newp)) {
|
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;
|
ok = m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()};
|
AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()};
|
||||||
|
// Don't check if typedef is to a <type T>::<reference> as might not be
|
||||||
|
// resolved yet
|
||||||
|
if (m_ds.m_dotPos == DP_NONE) checkDeclOrder(nodep, defp);
|
||||||
refp->typedefp(defp);
|
refp->typedefp(defp);
|
||||||
if (VN_IS(nodep->backp(), SelExtract)) {
|
if (VN_IS(nodep->backp(), SelExtract)) {
|
||||||
m_packedArrayDtp = refp;
|
m_packedArrayDtp = refp;
|
||||||
|
|
@ -4551,6 +4578,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
foundp = m_curSymp->findIdFlat(nodep->name());
|
foundp = m_curSymp->findIdFlat(nodep->name());
|
||||||
}
|
}
|
||||||
if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) {
|
if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) {
|
||||||
|
// Don't check if typedef is to a <type T>::<reference> as might not be resolved
|
||||||
|
// yet
|
||||||
|
if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp);
|
||||||
nodep->typedefp(defp);
|
nodep->typedefp(defp);
|
||||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||||
} else if (AstParamTypeDType* const defp
|
} else if (AstParamTypeDType* const defp
|
||||||
|
|
@ -4566,6 +4596,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||||
}
|
}
|
||||||
} else if (AstClass* const defp = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) {
|
} else if (AstClass* const defp = foundp ? VN_CAST(foundp->nodep(), Class) : nullptr) {
|
||||||
|
// Don't check if typedef is to a <type T>::<reference> as might not be resolved
|
||||||
|
// yet
|
||||||
|
if (!nodep->classOrPackagep()) checkDeclOrder(nodep, defp);
|
||||||
AstPin* const paramsp = nodep->paramsp();
|
AstPin* const paramsp = nodep->paramsp();
|
||||||
if (paramsp) paramsp->unlinkFrBackWithNext();
|
if (paramsp) paramsp->unlinkFrBackWithNext();
|
||||||
AstClassRefDType* const newp
|
AstClassRefDType* const newp
|
||||||
|
|
|
||||||
|
|
@ -782,19 +782,7 @@ class ParamProcessor final {
|
||||||
// Constify may have caused pinp->exprp to change
|
// Constify may have caused pinp->exprp to change
|
||||||
rawTypep = VN_AS(pinp->exprp(), NodeDType);
|
rawTypep = VN_AS(pinp->exprp(), NodeDType);
|
||||||
exprp = rawTypep->skipRefToNonRefp();
|
exprp = rawTypep->skipRefToNonRefp();
|
||||||
bool ok = true;
|
if (!modvarp->fwdType().isNodeCompatible(exprp)) {
|
||||||
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) {
|
|
||||||
pinp->v3error("Parameter type expression type "
|
pinp->v3error("Parameter type expression type "
|
||||||
<< exprp->prettyDTypeNameQ()
|
<< exprp->prettyDTypeNameQ()
|
||||||
<< " violates parameter's forwarding type '"
|
<< " violates parameter's forwarding type '"
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,7 @@ AstVar* V3ParseGrammar::createVariable(FileLine* fileline, const string& name,
|
||||||
}
|
}
|
||||||
if (VN_IS(dtypep, ParseTypeDType)) {
|
if (VN_IS(dtypep, ParseTypeDType)) {
|
||||||
// Parser needs to know what is a type
|
// 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<AstNode, AstNode>(nodep, newp);
|
AstNode::addNext<AstNode, AstNode>(nodep, newp);
|
||||||
}
|
}
|
||||||
// Don't set dtypep in the ranging;
|
// Don't set dtypep in the ranging;
|
||||||
|
|
|
||||||
|
|
@ -183,8 +183,8 @@ public:
|
||||||
PARSEP->tagNodep(nodep);
|
PARSEP->tagNodep(nodep);
|
||||||
return nodep;
|
return nodep;
|
||||||
}
|
}
|
||||||
AstNode* createTypedefFwd(FileLine* fl, const string& name) {
|
AstNode* createTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) {
|
||||||
AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name};
|
AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name, fwdType};
|
||||||
PARSEP->tagNodep(nodep);
|
PARSEP->tagNodep(nodep);
|
||||||
return nodep;
|
return nodep;
|
||||||
}
|
}
|
||||||
|
|
@ -2695,15 +2695,15 @@ type_declaration<nodep>: // ==IEEE: type_declaration
|
||||||
| yTYPEDEF idAny/*interface*/ '.' idAny/*type*/ idAny/*type*/ dtypeAttrListE ';'
|
| yTYPEDEF idAny/*interface*/ '.' idAny/*type*/ idAny/*type*/ dtypeAttrListE ';'
|
||||||
{ $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 typedef in this context"); }
|
{ $$ = nullptr; BBUNSUP($1, "Unsupported: SystemVerilog 2005 typedef in this context"); }
|
||||||
// // idAny as also allows redeclaring same typedef again
|
// // idAny as also allows redeclaring same typedef again
|
||||||
| yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>2, *$2); }
|
| yTYPEDEF idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>2, *$2, VFwdType::NONE); }
|
||||||
// // IEEE: expanded forward_type to prevent conflict
|
// // IEEE: expanded forward_type to prevent conflict
|
||||||
| yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3); }
|
| yTYPEDEF yENUM idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3, VFwdType::ENUM); }
|
||||||
| yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3); }
|
| yTYPEDEF ySTRUCT idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3, VFwdType::STRUCT); }
|
||||||
| yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3); }
|
| yTYPEDEF yUNION idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3, VFwdType::UNION); }
|
||||||
| yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3); }
|
| yTYPEDEF yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3, VFwdType::CLASS); }
|
||||||
| yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>4, *$4); }
|
| yTYPEDEF yINTERFACE yCLASS idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>4, *$4, VFwdType::INTERFACE_CLASS); }
|
||||||
//
|
//
|
||||||
| yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3); }
|
| yTYPEDEF error idAny ';' { $$ = GRAMMARP->createTypedefFwd($<fl>3, *$3, VFwdType::NONE); }
|
||||||
;
|
;
|
||||||
|
|
||||||
dtypeAttrListE<nodep>:
|
dtypeAttrListE<nodep>:
|
||||||
|
|
|
||||||
|
|
@ -534,7 +534,7 @@
|
||||||
"childDTypep": [
|
"childDTypep": [
|
||||||
{"type":"PARSETYPEDTYPE","name":"","addr":"(PI)","loc":"d,32:20,32:24","dtypep":"UNLINKED","generic":false}
|
{"type":"PARSETYPEDTYPE","name":"","addr":"(PI)","loc":"d,32:20,32:24","dtypep":"UNLINKED","generic":false}
|
||||||
],"delayp": [],"valuep": [],"attrsp": []},
|
],"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",
|
{"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": [
|
"childDTypep": [
|
||||||
{"type":"BASICDTYPE","name":"int","addr":"(SI)","loc":"d,33:17,33:20","dtypep":"(SI)","keyword":"int","range":"31:0","generic":false,"rangep": []}
|
{"type":"BASICDTYPE","name":"int","addr":"(SI)","loc":"d,33:17,33:20","dtypep":"(SI)","keyword":"int","range":"31:0","generic":false,"rangep": []}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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()
|
||||||
Loading…
Reference in New Issue