Support parameterized class references in extends statement (#4146)

This commit is contained in:
Ryszard Rozak 2023-04-24 23:25:53 +02:00 committed by GitHub
parent 621b7e63cf
commit ee5c0a2902
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 139 additions and 106 deletions

View File

@ -3287,7 +3287,7 @@ private:
iterateChildren(nodep);
}
void visit(AstClass* nodep) override {
nodep->user3SetOnce();
if (nodep->user3SetOnce()) return;
UINFO(5, " " << nodep << endl);
checkNoDot(nodep);
VL_RESTORER(m_curSymp);
@ -3300,117 +3300,116 @@ private:
m_ds.m_dotSymp = m_curSymp = m_modSymp = m_statep->getNodeSym(nodep);
m_modp = nodep;
int next = 0;
for (AstNode* itemp = nodep->extendsp(); itemp; itemp = itemp->nextp()) {
if (AstClassExtends* const cextp = VN_CAST(itemp, ClassExtends)) {
// Replace abstract reference with hard pointer
// Will need later resolution when deal with parameters
if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) {
cextp->v3error("Multiple inheritance illegal on non-interface classes"
" (IEEE 1800-2017 8.13)");
}
if (cextp->childDTypep() || cextp->dtypep()) {
// Already converted. Update symbol table to link unlinked members
importSymbolsFromExtended(nodep, cextp);
continue;
}
AstNode* cprp = cextp->classOrPkgsp();
VSymEnt* lookSymp = m_curSymp;
if (AstDot* const dotp = VN_CAST(cextp->classOrPkgsp(), Dot)) {
dotp->user3(true);
if (AstClassOrPackageRef* lookNodep
= VN_CAST(dotp->lhsp(), ClassOrPackageRef)) {
iterate(lookNodep);
cprp = dotp->rhsp();
lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep());
} else {
dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE
" using non-class under dot");
}
}
AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef);
if (VL_UNCOVERABLE(!cpackagerefp)) {
// Linking the extend gives an error before this is hit
cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
for (AstClassExtends* cextp = nodep->extendsp(); cextp;
cextp = VN_AS(cextp->nextp(), ClassExtends)) {
// Replace abstract reference with hard pointer
// Will need later resolution when deal with parameters
if (++next == 2 && !nodep->isInterfaceClass() && !cextp->isImplements()) {
cextp->v3error("Multiple inheritance illegal on non-interface classes"
" (IEEE 1800-2017 8.13)");
}
if (cextp->childDTypep() || cextp->dtypep()) {
// Already converted. Update symbol table to link unlinked members.
// Base class has to be visited in a case if its extends statement
// needs to be handled. Recursive inheritance was already checked.
iterate(cextp->classp());
importSymbolsFromExtended(nodep, cextp);
continue;
}
AstNode* cprp = cextp->classOrPkgsp();
VSymEnt* lookSymp = m_curSymp;
if (AstDot* const dotp = VN_CAST(cprp, Dot)) {
dotp->user3(true);
if (AstClassOrPackageRef* lookNodep
= VN_CAST(dotp->lhsp(), ClassOrPackageRef)) {
iterate(lookNodep);
cprp = dotp->rhsp();
lookSymp = m_statep->getNodeSym(lookNodep->classOrPackagep());
} else {
VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name());
if (foundp) {
AstClassRefDType* classRefDtypep = nullptr;
AstClass* classp = VN_CAST(foundp->nodep(), Class);
if (classp) {
if (classp != nodep) {
// Case with recursive inheritance is handled later in this
// function
iterate(classp);
}
if (m_statep->forPrimary()
&& m_extendsParam.find(classp) != m_extendsParam.end()) {
// Has a parameter as its base class
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) paramsp = paramsp->cloneTree(true);
classRefDtypep
= new AstClassRefDType{nodep->fileline(), classp, paramsp};
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
if (m_statep->forPrimary()) {
// Extending has to be handled after V3Param.cpp
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
continue;
} else {
AstNodeDType* const paramTypep = paramp->subDTypep();
classRefDtypep
= VN_CAST(paramTypep->cloneTree(false), ClassRefDType);
if (!classRefDtypep) {
paramTypep->v3error(
"Attempting to extend using non-class");
} else {
classp = classRefDtypep->classp();
}
}
} else {
cextp->v3warn(E_UNSUPPORTED,
"Unsupported: " << foundp->nodep()->prettyTypeName()
<< " in AstClassExtends");
dotp->lhsp()->v3error("Attempting to extend" // LCOV_EXCL_LINE
" using non-class under dot");
}
}
AstClassOrPackageRef* const cpackagerefp = VN_CAST(cprp, ClassOrPackageRef);
if (VL_UNCOVERABLE(!cpackagerefp)) {
// Linking the extend gives an error before this is hit
cextp->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
} else {
VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name());
if (foundp) {
AstClassRefDType* classRefDtypep = nullptr;
AstClass* classp = VN_CAST(foundp->nodep(), Class);
if (classp) {
if (classp != nodep) {
// Case with recursive inheritance is handled later in this
// function
iterate(classp);
}
if (classp) {
UINFO(8, "Import to " << nodep << " from export class " << classp
<< endl);
if (classp == nodep) {
cextp->v3error("Attempting to extend class "
<< nodep->prettyNameQ() << " from itself");
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
cextp->v3error(
"Attempting to implement from non-interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'extends'");
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
&& classp->isInterfaceClass()) {
cextp->v3error("Attempting to extend from interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'implements'");
if (m_statep->forPrimary()
&& m_extendsParam.find(classp) != m_extendsParam.end()) {
// Has a parameter as its base class
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) paramsp = paramsp->cloneTree(true);
classRefDtypep
= new AstClassRefDType{nodep->fileline(), classp, paramsp};
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
if (m_statep->forPrimary()) {
// Extending has to be handled after V3Param.cpp
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
continue;
} else {
AstNodeDType* const paramTypep = paramp->subDTypep();
classRefDtypep
= VN_CAST(paramTypep->cloneTree(false), ClassRefDType);
if (!classRefDtypep) {
paramTypep->v3error("Attempting to extend using non-class");
} else {
cextp->childDTypep(classRefDtypep);
classp->isExtended(true);
nodep->isExtended(true);
importSymbolsFromExtended(nodep, cextp);
VL_DO_DANGLING(
cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(),
cpackagerefp);
classp = classRefDtypep->classp();
}
}
} else {
const string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class for '"
<< cextp->verilogKwd() // extends/implements
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
cextp->v3warn(E_UNSUPPORTED,
"Unsupported: " << foundp->nodep()->prettyTypeName()
<< " in AstClassExtends");
}
if (classp) {
UINFO(8, "Import to " << nodep << " from export class " << classp
<< endl);
if (classp == nodep) {
cextp->v3error("Attempting to extend class "
<< nodep->prettyNameQ() << " from itself");
} else if (cextp->isImplements() && !classp->isInterfaceClass()) {
cextp->v3error("Attempting to implement from non-interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'extends'");
} else if (!cextp->isImplements() && !nodep->isInterfaceClass()
&& classp->isInterfaceClass()) {
cextp->v3error("Attempting to extend from interface class "
<< classp->prettyNameQ() << '\n'
<< "... Suggest use 'implements'");
} else {
cextp->childDTypep(classRefDtypep);
classp->isExtended(true);
nodep->isExtended(true);
importSymbolsFromExtended(nodep, cextp);
VL_DO_DANGLING(cextp->classOrPkgsp()->unlinkFrBack()->deleteTree(),
cpackagerefp);
}
}
} else {
const string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class for '"
<< cextp->verilogKwd() // extends/implements
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
}
}
}

View File

@ -39,17 +39,51 @@ module t (/*AUTOARG*/
endfunction
endclass
class ExtendBar1 extends Bar #(Foo);
function int get_x;
return super.get_x();
endfunction
function int get_6;
return 2 * get_3();
endfunction
endclass
class ExtendBarBaz extends Bar #(Baz);
function int get_x;
return super.get_x();
endfunction
function int get_8;
return 2 * get_4();
endfunction
endclass
class ExtendExtendBar extends ExtendBar;
function int get_12;
return 4 * get_3();
endfunction
endclass
Bar #() bar_foo_i;
Bar #(Baz) bar_baz_i;
ExtendBar extend_bar_i;
ExtendBar1 extend_bar1_i;
ExtendBarBaz extend_bar_baz_i;
ExtendExtendBar extend_extend_bar_i;
initial begin
bar_foo_i = new;
bar_baz_i = new;
extend_bar_i = new;
extend_bar1_i = new;
extend_bar_baz_i = new;
extend_extend_bar_i = new;
if (bar_foo_i.get_x() == 1 && bar_foo_i.get_3() == 3 &&
bar_baz_i.get_x() == 2 && bar_baz_i.get_4() == 4 &&
extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6) begin
extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 &&
extend_bar_i.get_x() == 1 && extend_bar_i.get_6() == 6 &&
extend_bar1_i.get_x() == 1 && extend_bar1_i.get_6() == 6 &&
extend_bar_baz_i.get_x() == 2 && extend_bar_baz_i.get_8() == 8 &&
extend_extend_bar_i.get_x() == 1 && extend_extend_bar_i.get_12() == 12) begin
$write("*-* All Finished *-*\n");
$finish;
end