Fix references to members of parameterized base classes (#4196)
This commit is contained in:
parent
46f719ceaa
commit
2ce7a348df
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue