Fix references to members of parameterized base classes (#4196)

This commit is contained in:
Ryszard Rozak 2023-05-16 01:50:04 +02:00 committed by GitHub
parent 46f719ceaa
commit 2ce7a348df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 180 additions and 108 deletions

View File

@ -809,6 +809,8 @@ class AstClassExtends final : public AstNode {
// @astgen op1 := childDTypep : Optional[AstNodeDType]
// @astgen op2 := classOrPkgsp : Optional[AstNode]
const bool m_isImplements = false; // class implements
bool m_parameterized = false; // has parameters in its statement
public:
AstClassExtends(FileLine* fl, AstNode* classOrPkgsp, bool isImplements)
: ASTGEN_SUPER_ClassExtends(fl)
@ -819,8 +821,12 @@ public:
void dump(std::ostream& str) const override;
bool hasDType() const override { return true; }
string verilogKwd() const override { return isImplements() ? "implements" : "extends"; }
AstClass* classp() const; // Class being extended (after link)
// Class being extended (after link and instantiation if needed)
AstClass* classOrNullp() const;
AstClass* classp() const; // Like above, but throws error if nulll
bool isImplements() const { return m_isImplements; }
void parameterized(bool flag) { m_parameterized = flag; }
bool parameterized() const { return m_parameterized; }
};
class AstClocking final : public AstNode {
// Parents: MODULE

View File

@ -1501,13 +1501,20 @@ void AstClassExtends::dump(std::ostream& str) const {
this->AstNode::dump(str);
if (isImplements()) str << " [IMPLEMENTS]";
}
AstClass* AstClassExtends::classp() const {
const AstClassRefDType* refp = VN_CAST(dtypep(), ClassRefDType);
if (VL_UNLIKELY(!refp)) { // LinkDot uses this for 'super.'
refp = VN_AS(childDTypep(), ClassRefDType);
AstClass* AstClassExtends::classOrNullp() const {
const AstNodeDType* const dtp = dtypep() ? dtypep() : childDTypep();
const AstClassRefDType* const refp = VN_CAST(dtp, ClassRefDType);
if (refp && !refp->paramsp()) {
// Class already resolved
return refp->classp();
} else {
return nullptr;
}
UASSERT_OBJ(refp, this, "class extends non-ref");
return refp->classp();
}
AstClass* AstClassExtends::classp() const {
AstClass* const clsp = classOrNullp();
UASSERT_OBJ(clsp, this, "Extended class is unresolved");
return clsp;
}
void AstClassRefDType::dump(std::ostream& str) const {
this->AstNodeDType::dump(str);

View File

@ -2020,9 +2020,10 @@ private:
int m_modportNum = 0; // Uniqueify modport numbers
bool m_inSens = false; // True if in senitem
std::set<std::string> m_ifClassImpNames; // Names imported from interface class
std::set<AstClass*> m_extendsParam; // Classes that has a parameter as its super class
bool m_insideClassExtParam = false; // Inside a class that extends a parameter.
// It may be set only in linkDotPrimary.
std::set<AstClass*> m_extendsParam; // Classes that have a parameterized super class
// (except the default instances)
// They are added to the set only in linkDotPrimary.
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution
@ -3273,6 +3274,86 @@ private:
// No checknodot(nodep), visit(AstScope) will check for LambdaArgRef
iterateChildren(nodep);
}
void visit(AstClassExtends* nodep) override {
// Resolve the symbol and get the class.
// If it is a parameterized case, the class will be resolved after V3Param.cpp
if (nodep->user3SetOnce()) return;
// If the class is resolved, there is nothing more to do
if (nodep->classOrNullp()) return;
if (m_statep->forPrimary()) {
AstNode* cprp = nodep->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 {
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
nodep->v3error("Attempting to extend using non-class"); // LCOV_EXCL_LINE
return;
}
VSymEnt* const foundp = lookSymp->findIdFallback(cpackagerefp->name());
if (foundp) {
if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) {
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) {
paramsp = paramsp->cloneTree(true);
nodep->parameterized(true);
}
nodep->childDTypep(new AstClassRefDType{nodep->fileline(), classp, paramsp});
// Link pins
iterate(nodep->childDTypep());
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
AstRefDType* const refParamp
= new AstRefDType{nodep->fileline(), paramp->name()};
refParamp->refDTypep(paramp);
nodep->childDTypep(refParamp);
nodep->parameterized(true);
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: " << foundp->nodep()->prettyTypeName()
<< " in AstClassExtends");
return;
}
} else {
const string suggest = m_statep->suggestSymFallback(
m_curSymp, cpackagerefp->name(), LinkNodeMatcherClass{});
cpackagerefp->v3error(
"Class for '" << nodep->verilogKwd() // extends/implements
<< "' not found: " << cpackagerefp->prettyNameQ() << '\n'
<< (suggest.empty() ? "" : cpackagerefp->warnMore() + suggest));
return;
}
if (!nodep->childDTypep()) nodep->v3error("Attempting to extend using non-class");
nodep->classOrPkgsp()->unlinkFrBack()->deleteTree();
} else {
// Probably a parameter
if (AstRefDType* const refp = VN_CAST(nodep->childDTypep(), RefDType)) {
if (AstClassRefDType* classRefp = VN_CAST(refp->skipRefp(), ClassRefDType)) {
// Resolved to a class reference.
refp->replaceWith(classRefp->cloneTree(false));
} else {
// Unable to resolve the ref type to a class reference.
// Get the value of type parameter passed to the class instance,
// to print the helpful error message.
const AstNodeDType* typep = refp->refDTypep();
if (const AstParamTypeDType* const paramp = VN_CAST(typep, ParamTypeDType)) {
typep = paramp->subDTypep();
}
typep->v3error("Attempting to extend using non-class");
}
}
}
}
void visit(AstClass* nodep) override {
if (nodep->user3SetOnce()) return;
UINFO(5, " " << nodep << endl);
@ -3295,110 +3376,50 @@ private:
cextp->v3error("Multiple inheritance illegal on non-interface classes"
" (IEEE 1800-2017 8.13)");
}
if (cextp->childDTypep() || cextp->dtypep()) {
iterate(cextp);
if (m_statep->forPrimary()) {
if (cextp->parameterized()) {
// Parameters in extends statement.
// The class can't be resolved in the current pass.
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
if (AstClassRefDType* const classRefp
= VN_CAST(cextp->childDTypep(), ClassRefDType)) {
AstClass* const classp = classRefp->classp();
if (classp != nodep) iterate(classp);
if (m_extendsParam.find(classp) != m_extendsParam.end()) {
// One of its super classes has parameters in extends statement.
// Some links may not be resolved in the first pass.
m_extendsParam.insert(nodep);
m_insideClassExtParam = true;
}
}
}
if (AstClass* const classp = cextp->classOrNullp()) {
// 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());
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'");
}
classp->isExtended(true);
nodep->isExtended(true);
iterate(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 {
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 (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");
}
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));
}
}
}
m_ds.m_dotSymp = m_curSymp;
@ -3491,6 +3512,8 @@ private:
nodep->replaceWith(newp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
} else if (m_insideClassExtParam) {
return;
} else {
if (foundp) UINFO(1, "Found sym node: " << foundp->nodep() << endl);
nodep->v3error("Can't find typedef: " << nodep->prettyNameQ());

View File

@ -21,16 +21,49 @@ endclass
typedef Cls#(8) Cls8_t;
class Getter1;
function int get_int;
return 1;
endfunction
endclass
class Getter2;
function int get_int;
return 2;
endfunction
endclass
class Foo #(type T=Getter1);
int x;
function new(int y);
x = y;
endfunction
endclass
class Bar #(type S=Getter2) extends Foo#(S);
T field;
function new(int y);
super.new(y);
field = new;
endfunction
function int get_field_int;
return field.get_int();
endfunction
endclass
// See also t_class_param_mod.v
module t (/*AUTOARG*/);
Cls #(.P(4)) c4;
Cls8_t c8;
Bar b;
initial begin
c4 = new;
c8 = new;
b = new(1);
if (c4.PBASE != 4) $stop;
if (c8.PBASE != 8) $stop;
if (c4.get_p() != 4) $stop;
@ -46,6 +79,9 @@ module t (/*AUTOARG*/);
$display("c4 = %s", $sformatf("%p", c4));
if ($sformatf("%p", c4) != "'{member:'ha}") $stop;
if (b.x != 1) $stop;
if (b.get_field_int() != 2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end