Fix localparam type assignment from interface type parameters (#6637) (#6732)

This commit is contained in:
em2machine
2025-12-06 09:42:59 -05:00
committed by GitHub
parent ba3794a495
commit c2cba8bfc6
54 changed files with 2469 additions and 79 deletions
+351 -71
View File
@@ -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;