mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
This commit is contained in:
+351
-71
@@ -67,12 +67,15 @@
|
||||
|
||||
#include "V3Global.h"
|
||||
#include "V3Graph.h"
|
||||
#include "V3LinkDotIfaceCapture.h"
|
||||
#include "V3MemberMap.h"
|
||||
#include "V3Parse.h"
|
||||
#include "V3Randomize.h"
|
||||
#include "V3String.h"
|
||||
#include "V3SymTable.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
@@ -217,9 +220,31 @@ public:
|
||||
UINFO(4, __FUNCTION__ << ": ");
|
||||
s_errorThisp = this;
|
||||
V3Error::errorExitCb(preErrorDumpHandler); // If get error, dump self
|
||||
const std::size_t capturedCount = V3LinkDotIfaceCapture::size();
|
||||
if (forPrimary()) {
|
||||
V3LinkDotIfaceCapture::enable(true);
|
||||
UINFO(9, "iface capture enabled for primary pass (persisting entries) size="
|
||||
<< capturedCount);
|
||||
} else if (forParamed()) {
|
||||
UINFO(9,
|
||||
"iface capture entering paramed pass captured typedef count=" << capturedCount);
|
||||
}
|
||||
readModNames();
|
||||
}
|
||||
~LinkDotState() {
|
||||
const std::size_t capturedCount = V3LinkDotIfaceCapture::size();
|
||||
if (forPrimary()) {
|
||||
UINFO(9,
|
||||
"iface capture leaving primary pass captured typedef count=" << capturedCount);
|
||||
} else if (forParamed()) {
|
||||
UINFO(9,
|
||||
"iface capture leaving paramed pass captured typedef count=" << capturedCount);
|
||||
if (capturedCount != 0) {
|
||||
UINFO(9, "iface capture warning: leftover captured typedef entries="
|
||||
<< capturedCount);
|
||||
}
|
||||
V3LinkDotIfaceCapture::reset();
|
||||
}
|
||||
V3Error::errorExitCb(nullptr);
|
||||
s_errorThisp = nullptr;
|
||||
}
|
||||
@@ -815,32 +840,31 @@ public:
|
||||
}
|
||||
static bool checkIfClassOrPackage(const VSymEnt* const symp) {
|
||||
if (VN_IS(symp->nodep(), Class) || VN_IS(symp->nodep(), Package)) return true;
|
||||
const AstRefDType* refDTypep = nullptr;
|
||||
|
||||
// Helper: check if a RefDType might resolve to a class later
|
||||
const auto checkUnresolvedRef = [](const AstRefDType* refp) -> bool {
|
||||
return refp && !refp->typeofp() && !refp->classOrPackageOpp();
|
||||
};
|
||||
|
||||
// Helper: allow types that can represent a class/package handle or an unresolved ref.
|
||||
// BasicDType is allowed here only for null/object handles.
|
||||
const auto isValidTypeNode = [](const AstNode* nodep) -> bool {
|
||||
return VN_IS(nodep, VoidDType) || VN_IS(nodep, BasicDType)
|
||||
|| VN_IS(nodep, ClassRefDType) || VN_IS(nodep, ParseRef);
|
||||
};
|
||||
|
||||
if (const AstTypedef* const typedefp = VN_CAST(symp->nodep(), Typedef)) {
|
||||
if (VN_IS(typedefp->childDTypep(), ClassRefDType)) return true;
|
||||
if (const AstRefDType* const refp = VN_CAST(typedefp->childDTypep(), RefDType)) {
|
||||
refDTypep = refp;
|
||||
}
|
||||
if (checkUnresolvedRef(VN_CAST(typedefp->childDTypep(), RefDType))) return true;
|
||||
} else if (const AstParamTypeDType* const paramTypep
|
||||
= VN_CAST(symp->nodep(), ParamTypeDType)) {
|
||||
if (const AstRequireDType* const requireDTypep
|
||||
= VN_CAST(paramTypep->childDTypep(), RequireDType)) {
|
||||
if (const AstRefDType* const refp = VN_CAST(requireDTypep->lhsp(), RefDType)) {
|
||||
refDTypep = refp;
|
||||
} else if (VN_IS(requireDTypep->lhsp(), VoidDType)
|
||||
|| VN_IS(requireDTypep->lhsp(), BasicDType)
|
||||
|| VN_IS(requireDTypep->lhsp(), ClassRefDType)) {
|
||||
return true;
|
||||
}
|
||||
// ParamTypeDType child may be wrapped in RequireDType or unwrapped
|
||||
AstNode* childp = paramTypep->childDTypep();
|
||||
if (const AstRequireDType* const reqp = VN_CAST(childp, RequireDType)) {
|
||||
childp = reqp->lhsp();
|
||||
}
|
||||
}
|
||||
// TODO: this should be handled properly - case when it is known what type is
|
||||
// referenced by AstRefDType (refDTypep->typeofp() is null or
|
||||
// refDTypep->classOrPackageOpp() is null)
|
||||
if (refDTypep && !refDTypep->typeofp() && !refDTypep->classOrPackageOpp()) {
|
||||
// When still unknown - return because it may be a class, classes may not be
|
||||
// linked at this point. Return in case it gets resolved to a class in the future
|
||||
return true;
|
||||
if (isValidTypeNode(childp)) return true;
|
||||
if (checkUnresolvedRef(VN_CAST(childp, RefDType))) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -2730,14 +2754,27 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
return superNewStmtp;
|
||||
}
|
||||
void checkNoDot(AstNode* nodep) {
|
||||
if (VL_UNLIKELY(!nodep)) {
|
||||
UINFO(9, indent() << "iface capture null node passed to checkNoDot; dot state="
|
||||
<< m_ds.ascii());
|
||||
m_ds.m_dotErr = true;
|
||||
return;
|
||||
}
|
||||
if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) {
|
||||
UINFO(9, indent() << "ds=" << m_ds.ascii());
|
||||
nodep->v3error("Syntax error: Not expecting "
|
||||
<< nodep->type() << " under a " << nodep->backp()->type()
|
||||
<< " in dotted expression\n"
|
||||
<< nodep->warnContextPrimary() << m_ds.m_dotp->warnOther()
|
||||
<< "... Resolving this reference\n"
|
||||
<< m_ds.m_dotp->warnContextSecondary());
|
||||
UINFO(9, indent() << "iface capture checkNoDot hit node=" << nodep
|
||||
<< " dotState=" << m_ds.ascii());
|
||||
if (VL_UNLIKELY(!m_ds.m_dotp)) {
|
||||
nodep->v3error("Syntax error: Not expecting " << nodep->type()
|
||||
<< " here (missing dot context)");
|
||||
} else {
|
||||
nodep->v3error("Syntax error: Not expecting "
|
||||
<< nodep->type() << " under a " << nodep->backp()->type()
|
||||
<< " in dotted expression\n"
|
||||
<< nodep->warnContextPrimary() << m_ds.m_dotp->warnOther()
|
||||
<< "... Resolving this reference\n"
|
||||
<< m_ds.m_dotp->warnContextSecondary());
|
||||
}
|
||||
m_ds.m_dotErr = true;
|
||||
}
|
||||
}
|
||||
@@ -2774,6 +2811,54 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
<< origp->warnContextSecondary());
|
||||
}
|
||||
}
|
||||
// This helper clones the RefDType (including the user2 context), wraps it in a ParamTypeDType
|
||||
// with the original var name, and returns the new dtype so the caller can ultimately replace
|
||||
// the var and continue typedef retargeting. (used in cases like: localparam rq_t =
|
||||
// bus_io.rq_t;)
|
||||
AstParamTypeDType* promoteVarToParamType(AstVar* varp, AstRefDType* typedefRefp) {
|
||||
if (!varp || !typedefRefp) return nullptr;
|
||||
VSymEnt* const varSymp = varp->user1u().toSymEnt();
|
||||
if (!varSymp) return nullptr;
|
||||
VSymEnt* const parentSymp = varSymp->parentp();
|
||||
if (!parentSymp) return nullptr;
|
||||
UINFO(9, indent() << "iface capture promote var to ParamType name=" << varp->prettyName()
|
||||
<< " dotState(before)=" << m_ds.ascii());
|
||||
AstParamTypeDType* const newTypep = new AstParamTypeDType{
|
||||
varp->fileline(), varp->varType(), VFwdType::NONE,
|
||||
varp->name(), VFlagChildDType{}, typedefRefp->cloneTree(false)};
|
||||
if (AstRefDType* const clonedRefp = VN_CAST(newTypep->childDTypep(), RefDType)) {
|
||||
clonedRefp->user2p(typedefRefp->user2p());
|
||||
if (V3LinkDotIfaceCapture::enabled()) {
|
||||
if (V3LinkDotIfaceCapture::replaceRef(typedefRefp, clonedRefp)) {
|
||||
UINFO(9, indent() << "iface capture retarget captured typedef var="
|
||||
<< varp->prettyName() << " orig=" << typedefRefp
|
||||
<< " clone=" << clonedRefp);
|
||||
}
|
||||
if (typedefRefp->user2p()) {
|
||||
UINFO(9, indent() << "iface capture capture recorded owner var="
|
||||
<< varp->prettyName() << " typedef=" << clonedRefp
|
||||
<< " cell=" << clonedRefp->user2p());
|
||||
}
|
||||
}
|
||||
}
|
||||
VSymEnt* const newSymEntp = new VSymEnt{m_statep->symsp(), newTypep};
|
||||
newSymEntp->parentp(parentSymp);
|
||||
newSymEntp->fallbackp(varSymp->fallbackp());
|
||||
newSymEntp->classOrPackagep(varSymp->classOrPackagep());
|
||||
newSymEntp->exported(varSymp->exported());
|
||||
newSymEntp->imported(varSymp->imported());
|
||||
newTypep->user1p(newSymEntp);
|
||||
parentSymp->reinsert(varp->name(), newSymEntp);
|
||||
varp->replaceWith(newTypep);
|
||||
// This conversion happens while linkDot is in the middle of a dotted lookup (e.g.
|
||||
// bus_io.rq_t). Reset the dot state so subsequent symbols in this scope do not inherit the
|
||||
// pending dot.
|
||||
m_ds.init(m_curSymp);
|
||||
UINFO(9, indent() << "iface capture converted owner var to ParamType name="
|
||||
<< varp->prettyName() << " dotState(after-reset)=" << m_ds.ascii());
|
||||
VL_DO_DANGLING(pushDeletep(varp), varp);
|
||||
return newTypep;
|
||||
}
|
||||
VSymEnt* getCreateClockingEventSymEnt(AstClocking* clockingp) {
|
||||
AstVar* const eventp = clockingp->ensureEventp(true);
|
||||
if (!eventp->user1p()) eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
||||
@@ -3121,6 +3206,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
iterateChildren(nodep);
|
||||
m_modp = nullptr;
|
||||
m_ds.m_dotSymp = m_curSymp = m_modSymp = nullptr;
|
||||
m_ds.m_dotPos = DP_NONE;
|
||||
m_ds.m_dotErr = false;
|
||||
}
|
||||
void visit(AstScope* nodep) override {
|
||||
LINKDOT_VISIT_START();
|
||||
@@ -3894,13 +3981,32 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
}
|
||||
} else if (AstTypedef* const defp = VN_CAST(foundp->nodep(), Typedef)) {
|
||||
ok = m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE;
|
||||
const bool ifaceFinalSegmentAllowed
|
||||
= (m_ds.m_dotPos == DP_FINAL) && m_ds.m_dotSymp
|
||||
&& VN_IS(m_ds.m_dotSymp->nodep(), Cell)
|
||||
&& VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp()
|
||||
&& VN_IS(VN_CAST(m_ds.m_dotSymp->nodep(), Cell)->modp(), Iface);
|
||||
ok = (m_ds.m_dotPos == DP_NONE || m_ds.m_dotPos == DP_SCOPE
|
||||
|| (V3LinkDotIfaceCapture::enabled() && ifaceFinalSegmentAllowed));
|
||||
if (V3LinkDotIfaceCapture::enabled() && ifaceFinalSegmentAllowed) {
|
||||
UINFO(9, indent() << "iface capture allow final-segment typedef name="
|
||||
<< nodep->name() << " dotText='" << m_ds.m_dotText
|
||||
<< "' dotSym=" << m_ds.m_dotSymp);
|
||||
}
|
||||
if (ok) {
|
||||
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);
|
||||
|
||||
V3LinkDotIfaceCapture::captureTypedefContext(
|
||||
refp, "typedef", static_cast<int>(m_ds.m_dotPos),
|
||||
m_ds.m_dotPos == DP_FINAL, m_ds.m_dotText, m_ds.m_dotSymp, m_curSymp,
|
||||
m_modp, nodep,
|
||||
[this](AstVar* v, AstRefDType* r) { return promoteVarToParamType(v, r); },
|
||||
[this]() { return indent(); });
|
||||
|
||||
if (VN_IS(nodep->backp(), SelExtract)) {
|
||||
m_packedArrayDtp = refp;
|
||||
} else {
|
||||
@@ -3913,6 +4019,14 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
if (ok) {
|
||||
AstRefDType* const refp = new AstRefDType{nodep->fileline(), nodep->name()};
|
||||
refp->refDTypep(defp);
|
||||
|
||||
V3LinkDotIfaceCapture::captureTypedefContext(
|
||||
refp, "paramtype", static_cast<int>(m_ds.m_dotPos),
|
||||
m_ds.m_dotPos == DP_FINAL, m_ds.m_dotText, m_ds.m_dotSymp, m_curSymp,
|
||||
m_modp, nodep,
|
||||
[this](AstVar* v, AstRefDType* r) { return promoteVarToParamType(v, r); },
|
||||
[this]() { return indent(); });
|
||||
|
||||
replaceWithCheckBreak(nodep, refp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
@@ -4987,8 +5101,52 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
m_ds.m_dotSymp = VL_RESTORER_PREV(m_curSymp);
|
||||
}
|
||||
void visit(AstRefDType* nodep) override {
|
||||
|
||||
if (auto* const typeOfp = nodep->typeofp()) {
|
||||
iterate(typeOfp);
|
||||
// After iteration, typeofp() may have been replaced with a resolved type
|
||||
// (e.g., DOT expression like if0.rq_t resolved to REFDTYPE)
|
||||
// If it's now a NodeDType, replace this RefDType with it
|
||||
if (AstNodeDType* const resolvedDTypep = VN_CAST(nodep->typeofp(), NodeDType)) {
|
||||
UINFO(9,
|
||||
indent() << "iface capture typeofp resolved to dtype, replacing RefDType\n");
|
||||
resolvedDTypep->unlinkFrBack();
|
||||
nodep->replaceWith(resolvedDTypep);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
// If the resolved dtype is a RefDType with an interface typedef,
|
||||
// ensure it's captured for re-resolution during paramed pass
|
||||
if (AstRefDType* const resolvedRefp = VN_CAST(resolvedDTypep, RefDType)) {
|
||||
if (resolvedRefp->user2p() && !V3LinkDotIfaceCapture::find(resolvedRefp)) {
|
||||
AstCell* const cellp = VN_AS(resolvedRefp->user2p(), Cell);
|
||||
UINFO(9, indent() << "iface capture re-capture resolved RefDType="
|
||||
<< resolvedRefp << " cell=" << cellp << "\n");
|
||||
V3LinkDotIfaceCapture::add(resolvedRefp, cellp, m_modp,
|
||||
resolvedRefp->typedefp());
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve its reference
|
||||
if (nodep->user3SetOnce()) return;
|
||||
if (V3LinkDotIfaceCapture::find(nodep)) {
|
||||
UINFO(9, indent() << "iface capture visit captured typedef ptr=" << nodep
|
||||
<< " user2=" << nodep->user2p());
|
||||
}
|
||||
if (m_statep->forParamed() && nodep->user3()) {
|
||||
if (V3LinkDotIfaceCapture::enabled() && nodep->user2p()) {
|
||||
UINFO(9, indent() << "iface capture clear user3 for captured typedef name="
|
||||
<< nodep->name() << " cell=" << nodep->user2p());
|
||||
}
|
||||
nodep->user3(false);
|
||||
}
|
||||
if (nodep->user3SetOnce()) {
|
||||
if (V3LinkDotIfaceCapture::enabled() && nodep->user2p()) {
|
||||
UINFO(9, indent() << "iface capture skip revisit name=" << nodep->name()
|
||||
<< " already user3 and captured cell=" << nodep->user2p());
|
||||
}
|
||||
return;
|
||||
}
|
||||
LINKDOT_VISIT_START();
|
||||
UINFO(5, indent() << "visit " << nodep);
|
||||
if (AstNode* const cpackagep = nodep->classOrPackageOpp()) {
|
||||
@@ -5030,19 +5188,80 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
}
|
||||
VL_DO_DANGLING(pushDeletep(cpackagep->unlinkFrBack()), cpackagep);
|
||||
}
|
||||
|
||||
const bool capEnable = V3LinkDotIfaceCapture::enabled();
|
||||
const auto* const capEntryp = capEnable ? V3LinkDotIfaceCapture::find(nodep) : nullptr;
|
||||
const bool captureMapHit = capEntryp != nullptr;
|
||||
AstCell* const captureEntryCellp = capEntryp ? capEntryp->cellp : nullptr;
|
||||
AstTypedef* const capturedTypedefp = capEntryp ? capEntryp->typedefp : nullptr;
|
||||
const VSymEnt* const capturedTypedefSymp
|
||||
= capturedTypedefp ? m_statep->getNodeSym(capturedTypedefp) : nullptr;
|
||||
|
||||
const bool ifaceCaptured = capEnable && nodep->user2p();
|
||||
const bool missingIfaceContext = captureMapHit && !ifaceCaptured;
|
||||
const char* const passLabel = m_statep->forParamed() ? "paramed" : "primary";
|
||||
if (missingIfaceContext) {
|
||||
UINFO(9, indent() << "iface capture captured typedef missing user2 name="
|
||||
<< nodep->name() << " ref=" << nodep << " pass=" << passLabel
|
||||
<< " entryCell=" << captureEntryCellp);
|
||||
}
|
||||
AstCell* const capturedCellp = ifaceCaptured ? VN_CAST(nodep->user2p(), Cell) : nullptr;
|
||||
|
||||
bool forcedIfaceDotScope = false;
|
||||
bool resolvedCapturedTypedef = false;
|
||||
bool captureEntryRetired = false;
|
||||
const auto retireCapture = [&](const char* reason) {
|
||||
if (!ifaceCaptured || captureEntryRetired) return;
|
||||
const auto* entry = V3LinkDotIfaceCapture::find(nodep);
|
||||
AstCell* const entryCell = entry ? entry->cellp : nullptr;
|
||||
UINFO(9, indent() << "iface capture retire captured typedef reason=" << reason
|
||||
<< " name=" << nodep->name() << " pass=" << passLabel
|
||||
<< " user2=" << nodep->user2p() << " entryCell=" << entryCell);
|
||||
const bool erased = V3LinkDotIfaceCapture::erase(nodep);
|
||||
captureEntryRetired = true;
|
||||
UINFO(9, indent() << "iface capture retire erase result name=" << nodep->name()
|
||||
<< " erased=" << erased);
|
||||
};
|
||||
if (ifaceCaptured && m_statep->forParamed()) {
|
||||
UINFO(9, indent() << "iface capture captured typedef name=" << nodep->name()
|
||||
<< " typedef=" << nodep->typedefp() << " cell=" << capturedCellp);
|
||||
if (nodep->typedefp()) {
|
||||
UINFO(9, indent() << "iface capture refresh typedef binding name=" << nodep->name()
|
||||
<< " typedef=" << nodep->typedefp()
|
||||
<< " cell=" << capturedCellp);
|
||||
nodep->typedefp(nullptr);
|
||||
nodep->classOrPackagep(nullptr);
|
||||
}
|
||||
}
|
||||
if (m_ds.m_dotp && (m_ds.m_dotPos == DP_PACKAGE || m_ds.m_dotPos == DP_SCOPE)) {
|
||||
UASSERT_OBJ(VN_IS(m_ds.m_dotp->lhsp(), ClassOrPackageRef), m_ds.m_dotp->lhsp(),
|
||||
"Bad package link");
|
||||
auto* const cpackagerefp = VN_AS(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||
auto* const cpackagerefp = VN_CAST(m_ds.m_dotp->lhsp(), ClassOrPackageRef);
|
||||
UASSERT_OBJ(cpackagerefp->classOrPackageSkipp(), m_ds.m_dotp->lhsp(),
|
||||
"Bad package link");
|
||||
nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
} else {
|
||||
} else if (!ifaceCaptured) {
|
||||
checkNoDot(nodep);
|
||||
} else {
|
||||
UINFO(9, indent() << "iface capture consume captured iface context name="
|
||||
<< nodep->name() << " cell=" << capturedCellp);
|
||||
m_ds.m_dotPos = DP_SCOPE;
|
||||
forcedIfaceDotScope = true;
|
||||
// Set dotSymp to the cell's symbol entry so lookup happens in the interface scope
|
||||
if (capturedCellp && m_statep->existsNodeSym(capturedCellp)) {
|
||||
VSymEnt* const cellSymp = m_statep->getNodeSym(capturedCellp);
|
||||
m_ds.m_dotSymp = cellSymp;
|
||||
UINFO(9, indent() << "iface capture set dotSymp to cell scope cellSymp="
|
||||
<< cellSymp << " node=" << cellSymp->nodep());
|
||||
}
|
||||
}
|
||||
if (nodep->typeofp()) { // Really is a typeof not a reference
|
||||
} else if (!nodep->typedefp() && !nodep->subDTypep()) {
|
||||
if (!nodep->typedefp() && !nodep->subDTypep()) {
|
||||
if (ifaceCaptured) {
|
||||
UINFO(9, indent() << "iface capture lookup start name=" << nodep->name()
|
||||
<< " dotPos=" << static_cast<int>(m_ds.m_dotPos) << " dotSym="
|
||||
<< m_ds.m_dotSymp << " classPkg=" << nodep->classOrPackagep());
|
||||
}
|
||||
const VSymEnt* foundp;
|
||||
if (nodep->classOrPackagep()) {
|
||||
foundp = m_statep->getNodeSym(nodep->classOrPackagep())->findIdFlat(nodep->name());
|
||||
@@ -5052,52 +5271,105 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
||||
} else if (m_ds.m_dotPos == DP_FIRST || m_ds.m_dotPos == DP_NONE) {
|
||||
foundp = m_curSymp->findIdFallback(nodep->name());
|
||||
} else {
|
||||
foundp = m_curSymp->findIdFlat(nodep->name());
|
||||
//foundp = m_curSymp->findIdFlat(nodep->name());
|
||||
// Use dotSymp if set (e.g., for captured interface typedefs), else curSymp
|
||||
VSymEnt* const lookupSymp = m_ds.m_dotSymp ? m_ds.m_dotSymp : m_curSymp;
|
||||
foundp = lookupSymp->findIdFlat(nodep->name());
|
||||
}
|
||||
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() && !defp->isUnderClass())
|
||||
checkDeclOrder(nodep, defp);
|
||||
nodep->typedefp(defp);
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
} else if (AstParamTypeDType* const defp
|
||||
= foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) {
|
||||
if (defp == nodep->backp()) { // Where backp is typically typedef
|
||||
nodep->v3error("Reference to '" << m_ds.m_dotText
|
||||
<< (m_ds.m_dotText == "" ? "" : ".")
|
||||
<< nodep->prettyName() << "'"
|
||||
<< " type would form a recursive definition");
|
||||
nodep->refDTypep(nodep->findVoidDType()); // Try to reduce later errors
|
||||
} else {
|
||||
nodep->refDTypep(defp);
|
||||
if (!foundp && ifaceCaptured && capturedTypedefp) {
|
||||
UINFO(9, indent() << "iface capture binding via captured typedef fallback name="
|
||||
<< nodep->name() << " typedef=" << capturedTypedefp);
|
||||
nodep->typedefp(capturedTypedefp);
|
||||
nodep->classOrPackagep(capturedTypedefSymp ? capturedTypedefSymp->classOrPackagep()
|
||||
: nullptr);
|
||||
resolvedCapturedTypedef = true;
|
||||
}
|
||||
if (!resolvedCapturedTypedef && foundp) {
|
||||
VSymEnt* const parentSymp = foundp->parentp();
|
||||
UINFO(9, indent() << "iface capture resolved typedef name=" << nodep->name()
|
||||
<< " foundNode=" << foundp->nodep() << " parentNode="
|
||||
<< (parentSymp ? parentSymp->nodep() : nullptr));
|
||||
}
|
||||
if (!resolvedCapturedTypedef) {
|
||||
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() && !defp->isUnderClass())
|
||||
checkDeclOrder(nodep, defp);
|
||||
nodep->typedefp(defp);
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
}
|
||||
} 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();
|
||||
if (paramsp) paramsp->unlinkFrBackWithNext();
|
||||
AstClassRefDType* const newp
|
||||
= new AstClassRefDType{nodep->fileline(), defp, paramsp};
|
||||
newp->classOrPackagep(foundp->classOrPackagep());
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (m_insideClassExtParam) {
|
||||
return;
|
||||
} else {
|
||||
if (foundp) {
|
||||
UINFO(1, "Found sym node: " << foundp->nodep());
|
||||
nodep->v3error("Expecting a data type: " << nodep->prettyNameQ());
|
||||
resolvedCapturedTypedef = true;
|
||||
} else if (AstParamTypeDType* const defp
|
||||
= foundp ? VN_CAST(foundp->nodep(), ParamTypeDType) : nullptr) {
|
||||
if (defp == nodep->backp()) { // Where backp is typically typedef
|
||||
nodep->v3error("Reference to '"
|
||||
<< m_ds.m_dotText << (m_ds.m_dotText == "" ? "" : ".")
|
||||
<< nodep->prettyName() << "'"
|
||||
<< " type would form a recursive definition");
|
||||
nodep->refDTypep(nodep->findVoidDType()); // Try to reduce later errors
|
||||
} else {
|
||||
nodep->refDTypep(defp);
|
||||
nodep->classOrPackagep(foundp->classOrPackagep());
|
||||
resolvedCapturedTypedef = true;
|
||||
}
|
||||
} 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();
|
||||
if (paramsp) paramsp->unlinkFrBackWithNext();
|
||||
AstClassRefDType* const newp
|
||||
= new AstClassRefDType{nodep->fileline(), defp, paramsp};
|
||||
newp->classOrPackagep(foundp->classOrPackagep());
|
||||
resolvedCapturedTypedef = true;
|
||||
retireCapture("resolved"); // Must retire before replacing node
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
} else if (m_insideClassExtParam) {
|
||||
return;
|
||||
} else {
|
||||
nodep->v3error("Can't find typedef/interface: " << nodep->prettyNameQ());
|
||||
if (foundp) {
|
||||
UINFO(1, "Found sym node: " << foundp->nodep());
|
||||
nodep->v3error("Expecting a data type: " << nodep->prettyNameQ());
|
||||
} else {
|
||||
nodep->v3error("Can't find typedef/interface: " << nodep->prettyNameQ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (forcedIfaceDotScope && m_ds.m_dotPos == DP_SCOPE && !m_ds.m_dotp) {
|
||||
UINFO(9, indent() << "iface capture reset dot state after captured typedef name="
|
||||
<< nodep->name());
|
||||
m_ds.init(m_curSymp);
|
||||
}
|
||||
if (ifaceCaptured && resolvedCapturedTypedef) { retireCapture("resolved"); }
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstRequireDType* nodep) override {
|
||||
// Handle type validation for localparam type assignments
|
||||
// Iterate child to resolve any ParseRef nodes
|
||||
iterateChildren(nodep);
|
||||
// Only emit error if the child is not a type.
|
||||
// Do NOT unwrap valid types here - leave that to V3Width.
|
||||
// Unwrapping here breaks type parameter resolution during cloning.
|
||||
if (nodep->lhsp() && !VN_IS(nodep->lhsp(), NodeDType)) {
|
||||
// Not a type - emit error
|
||||
if (AstConst* const constp = VN_CAST(nodep->lhsp(), Const)) {
|
||||
nodep->lhsp()->v3error(
|
||||
"Expecting a data type, not a constant: " << constp->toSInt());
|
||||
} else {
|
||||
nodep->lhsp()->v3error("Expecting a data type, not "
|
||||
<< nodep->lhsp()->typeName() << ": '"
|
||||
<< nodep->lhsp()->prettyName() << "'");
|
||||
}
|
||||
// Replace with void to keep AST valid and allow clean exit
|
||||
nodep->replaceWith(new AstVoidDType{nodep->fileline()});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstDpiExport* nodep) override {
|
||||
// AstDpiExport: Make sure the function referenced exists, then dump it
|
||||
LINKDOT_VISIT_START();
|
||||
@@ -5234,6 +5506,14 @@ public:
|
||||
LinkDotResolveVisitor(AstNetlist* rootp, LinkDotState* statep)
|
||||
: m_statep{statep} {
|
||||
UINFO(4, __FUNCTION__ << ": ");
|
||||
|
||||
if (m_statep->forParamed()) {
|
||||
V3LinkDotIfaceCapture::forEach(
|
||||
[](const V3LinkDotIfaceCapture::CapturedIfaceTypedef& entry) {
|
||||
if (AstRefDType* const refp = entry.refp) refp->user3(false);
|
||||
});
|
||||
}
|
||||
|
||||
iterate(rootp);
|
||||
std::map<std::string, AstNodeModule*> modulesToRevisit = std::move(m_modulesToRevisit);
|
||||
m_lastDeferredp = nullptr;
|
||||
|
||||
Reference in New Issue
Block a user