Fix class extend param references (#4136)

Signed-off-by: Ryszard Rozak <rrozak@antmicro.com>
This commit is contained in:
Ryszard Rozak 2023-04-20 13:11:35 +02:00 committed by GitHub
parent 65a484e00b
commit 84a46939b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 64 additions and 50 deletions

View File

@ -2035,6 +2035,8 @@ private:
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.
struct DotStates {
DotPosition m_dotPos; // Scope part of dotted resolution
@ -2238,26 +2240,6 @@ private:
if (classp->isInterfaceClass()) importImplementsClass(nodep, srcp, classp);
if (!cextp->isImplements()) m_curSymp->importFromClass(m_statep->symsp(), srcp);
}
void visitGlobalClassParams(AstClass* const nodep) {
// Parameters from port parameter list are at the beginning of the members list, so if a
// node, that isn't a parameter, is encountered, all nodes after aren't global parameters
// too.
for (AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) {
// Global parameters are at the beginning of the members list, so if a node, that isn't
// a global parameter, is encountered, all nodes after it aren't global parameters too.
bool globalParam = false;
if (const AstParamTypeDType* const parp = VN_CAST(stmtp, ParamTypeDType)) {
if (parp->isGParam()) globalParam = true;
} else if (const AstVar* const varp = VN_CAST(stmtp, Var)) {
if (varp->isGParam()) globalParam = true;
}
if (globalParam) {
iterate(stmtp);
} else {
break;
}
}
}
// VISITs
void visit(AstNetlist* nodep) override {
@ -2424,12 +2406,17 @@ private:
nodep->v3error("'super' used on non-extended class (IEEE 1800-2017 8.15)");
m_ds.m_dotErr = true;
} else {
const auto cextp = classp->extendsp();
UASSERT_OBJ(cextp, nodep, "Bad super extends link");
const auto sclassp = cextp->classp();
UASSERT_OBJ(sclassp, nodep, "Bad superclass");
m_ds.m_dotSymp = m_statep->getNodeSym(sclassp);
UINFO(8, " super. " << m_ds.ascii() << endl);
if (m_statep->forPrimary()
&& m_extendsParam.find(classp) != m_extendsParam.end()) {
m_ds.m_unresolvedClass = true;
} else {
const auto cextp = classp->extendsp();
UASSERT_OBJ(cextp, nodep, "Bad super extends link");
const auto sclassp = cextp->classp();
UASSERT_OBJ(sclassp, nodep, "Bad superclass");
m_ds.m_dotSymp = m_statep->getNodeSym(sclassp);
UINFO(8, " super. " << m_ds.ascii() << endl);
}
}
}
} else if (VN_IS(nodep->lhsp(), ClassOrPackageRef)) {
@ -2798,8 +2785,10 @@ private:
}
}
}
//
if (!ok) {
// Don't throw error ifthe reference is inside a class that extends a param, because
// some members can't be linked in such a case. m_insideClassExtParam may be true only
// in the first stage of linking.
if (!ok && !m_insideClassExtParam) {
// Cells/interfaces can't be implicit
const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
const bool checkImplicit = (!m_ds.m_dotp && m_ds.m_dotText == "" && !isCell);
@ -3110,6 +3099,10 @@ private:
nodep->taskp(taskp);
nodep->classOrPackagep(foundp->classOrPackagep());
UINFO(7, " Resolved " << nodep << endl); // Also prints taskp
} else if (m_insideClassExtParam) {
// The reference may point to a method declared in a super class, which is proved
// by a parameter. In such a case, it can't be linked at the first stage.
return;
} else {
// Note ParseRef has similar error handling/message output
UINFO(7, " ErrFtask curSymp=se" << cvtToHex(m_curSymp) << " dotSymp=se"
@ -3300,6 +3293,7 @@ private:
VL_RESTORER(m_curSymp);
VL_RESTORER(m_modSymp);
VL_RESTORER(m_ifClassImpNames);
VL_RESTORER(m_insideClassExtParam);
{
m_ds.init(m_curSymp);
// Until overridden by a SCOPE
@ -3352,10 +3346,7 @@ private:
&& m_extendsParam.find(classp) != m_extendsParam.end()) {
// Has a parameter as its base class
m_extendsParam.insert(nodep);
// Link global parameters. They are declared before the extends
// statement, so should be unrelated to the base class
visitGlobalClassParams(nodep);
return;
m_insideClassExtParam = true;
}
AstPin* paramsp = cpackagerefp->paramsp();
if (paramsp) paramsp = paramsp->cloneTree(true);
@ -3364,13 +3355,12 @@ private:
} else if (AstParamTypeDType* const paramp
= VN_CAST(foundp->nodep(), ParamTypeDType)) {
if (m_statep->forPrimary()) {
// Extending has to be handled after V3Param.cpp, but the type
// reference has to be visited
iterate(paramp);
// Extending has to be handled after V3Param.cpp
m_extendsParam.insert(nodep);
return;
m_insideClassExtParam = true;
continue;
} else {
AstNodeDType* const paramTypep = paramp->getChildDTypep();
AstNodeDType* const paramTypep = paramp->subDTypep();
classRefDtypep
= VN_CAST(paramTypep->cloneTree(false), ClassRefDType);
if (!classRefDtypep) {

View File

@ -949,16 +949,18 @@ class ParamVisitor final : public VNVisitor {
// Process once; note user5 will be cleared on specialization, so we will do the
// specialized module if needed
if (modp->user5SetOnce()) continue;
if (!modp->user5SetOnce()) {
// TODO: this really should be an assert, but classes and hier_blocks are special...
if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName());
// TODO: this really should be an assert, but classes and hier_blocks are
// special...
if (modp->someInstanceName().empty()) modp->someInstanceName(modp->origName());
// Iterate the body
{
VL_RESTORER(m_modp);
m_modp = modp;
iterateChildren(modp);
// Iterate the body
{
VL_RESTORER(m_modp);
m_modp = modp;
iterateChildren(modp);
}
}
// Process interface cells, then non-interface cells, which may reference an interface

View File

@ -30,7 +30,7 @@ module t (/*AUTOARG*/
endfunction
endclass
class ExtendBar extends Bar;
class ExtendBar extends Bar#();
function int get_x;
return super.get_x();
endfunction
@ -39,7 +39,7 @@ module t (/*AUTOARG*/
endfunction
endclass
Bar bar_foo_i;
Bar #() bar_foo_i;
Bar #(Baz) bar_baz_i;
ExtendBar extend_bar_i;

View File

@ -52,7 +52,7 @@ class Foo #(type IF=Empty) extends IF;
int a = 1;
endclass
class Bar #(type A=int, type B=A) extends Foo;
class Bar #(type A=int, type B=A) extends Foo#();
function int get_size_A;
return $bits(A);
endfunction
@ -64,16 +64,32 @@ endclass
class Empty2;
endclass
class Baz #(type T=Empty2) extends Foo;
class Baz #(type T=Empty2) extends Foo#();
endclass
class Getter1 extends Baz;
class Getter1 extends Baz#();
function int get_1;
foo_t f = new;
return f.a;
endfunction
endclass
class MyInt1;
int x = 1;
endclass
class MyInt2;
int x = 2;
endclass
class ExtendsMyInt #(type T=MyInt1) extends T;
typedef ExtendsMyInt#(T) this_type;
function int get_this_type_x;
this_type t = new;
return t.x;
endfunction
endclass
module t (/*AUTOARG*/);
initial begin
@ -93,6 +109,9 @@ module t (/*AUTOARG*/);
automatic Getter1 getter1 = new;
automatic ExtendsMyInt#() ext1 = new;
automatic ExtendsMyInt#(MyInt2) ext2 = new;
typedef bit my_bit_t;
Bar#(.A(my_bit_t)) bar_a_bit = new;
Bar#(.B(my_bit_t)) bar_b_bit = new;
@ -123,6 +142,9 @@ module t (/*AUTOARG*/);
if (getter1.get_1() != 1) $stop;
if (ext1.get_this_type_x() != 1) $stop;
if (ext2.get_this_type_x() != 2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end