Fix selects of static members (#4326)
This commit is contained in:
parent
32019d2bc4
commit
242f661644
|
|
@ -1428,6 +1428,7 @@ class AstMemberSel final : public AstNodeExpr {
|
|||
// @astgen op1 := fromp : AstNodeExpr
|
||||
// Don't need the class we are extracting from, as the "fromp()"'s datatype can get us to it
|
||||
string m_name;
|
||||
VAccess m_access; // Read or write, as in AstNodeVarRef
|
||||
AstVar* m_varp = nullptr; // Post link, variable within class that is target of selection
|
||||
public:
|
||||
AstMemberSel(FileLine* fl, AstNodeExpr* fromp, VFlagChildDType, const string& name)
|
||||
|
|
@ -1448,10 +1449,12 @@ public:
|
|||
void dump(std::ostream& str) const override;
|
||||
string name() const override VL_MT_STABLE { return m_name; }
|
||||
void name(const string& name) override { m_name = name; }
|
||||
VAccess access() const { return m_access; }
|
||||
void access(const VAccess& flag) { m_access = flag; }
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool same(const AstNode* samep) const override { return true; } // dtype comparison does it
|
||||
bool same(const AstNode* samep) const override;
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
AstVar* varp() const { return m_varp; }
|
||||
void varp(AstVar* nodep) { m_varp = nodep; }
|
||||
|
|
|
|||
|
|
@ -1676,6 +1676,12 @@ AstNodeUOrStructDType* AstMemberDType::getChildStructp() const {
|
|||
return VN_CAST(subdtp, NodeUOrStructDType); // Maybe nullptr
|
||||
}
|
||||
|
||||
bool AstMemberSel::same(const AstNode* samep) const {
|
||||
const AstMemberSel* const sp = static_cast<const AstMemberSel*>(samep);
|
||||
return sp != nullptr && access() == sp->access() && fromp()->same(sp->fromp())
|
||||
&& name() == sp->name() && varp()->same(sp->varp());
|
||||
}
|
||||
|
||||
void AstMemberSel::dump(std::ostream& str) const {
|
||||
this->AstNodeExpr::dump(str);
|
||||
str << " -> ";
|
||||
|
|
|
|||
|
|
@ -275,6 +275,16 @@ private:
|
|||
iterateAndNextNull(nodep->thsp());
|
||||
}
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_setRefLvalue != VAccess::NOCHANGE) {
|
||||
nodep->access(m_setRefLvalue);
|
||||
} else {
|
||||
// It is the only place where the access is set to member select nodes.
|
||||
// If it doesn't have to be set to WRITE, it means that it is READ.
|
||||
nodep->access(VAccess::READ);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
VL_RESTORER(m_ftaskp);
|
||||
m_ftaskp = nodep;
|
||||
|
|
|
|||
|
|
@ -2710,31 +2710,7 @@ private:
|
|||
if (AstNodeUOrStructDType* const adtypep = VN_CAST(fromDtp, NodeUOrStructDType)) {
|
||||
if (memberSelStruct(nodep, adtypep)) return;
|
||||
} else if (AstClassRefDType* const adtypep = VN_CAST(fromDtp, ClassRefDType)) {
|
||||
if (AstNode* const foundp = memberSelClass(nodep, adtypep)) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||
nodep->dtypep(foundp->dtypep());
|
||||
nodep->varp(varp);
|
||||
return;
|
||||
}
|
||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||
nodep->replaceWith(adfoundp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
|
||||
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), nullptr});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
UINFO(1, "found object " << foundp << endl);
|
||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< foundp->warnOther() << "... Location of found object\n"
|
||||
<< foundp->warnContextSecondary());
|
||||
}
|
||||
if (memberSelClass(nodep, adtypep)) return;
|
||||
} else if (AstIfaceRefDType* const adtypep = VN_CAST(fromDtp, IfaceRefDType)) {
|
||||
if (AstNode* const foundp = memberSelIface(nodep, adtypep)) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
|
|
@ -2773,15 +2749,50 @@ private:
|
|||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
AstNode* memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
|
||||
// Returns node if ok
|
||||
bool memberSelClass(AstMemberSel* nodep, AstClassRefDType* adtypep) {
|
||||
// Returns true if ok
|
||||
// No need to width-resolve the class, as it was done when we did the child
|
||||
AstClass* const first_classp = adtypep->classp();
|
||||
UASSERT_OBJ(first_classp, nodep, "Unlinked");
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
if (AstNode* const foundp = classp->findMember(nodep->name())) return foundp;
|
||||
if (AstNode* const foundp = classp->findMember(nodep->name())) {
|
||||
if (AstVar* const varp = VN_CAST(foundp, Var)) {
|
||||
if (!varp->didWidth()) userIterate(varp, nullptr);
|
||||
if (varp->lifetime().isStatic()) {
|
||||
// Static fiels are moved outside the class, so they shouldn't be accessed
|
||||
// by member select on a class object
|
||||
AstVarRef* const varRefp
|
||||
= new AstVarRef{nodep->fileline(), varp, nodep->access()};
|
||||
varRefp->classOrPackagep(classp);
|
||||
nodep->replaceWith(varRefp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
nodep->dtypep(foundp->dtypep());
|
||||
nodep->varp(varp);
|
||||
return true;
|
||||
}
|
||||
if (AstEnumItemRef* const adfoundp = VN_CAST(foundp, EnumItemRef)) {
|
||||
nodep->replaceWith(adfoundp->cloneTree(false));
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
if (AstNodeFTask* const methodp = VN_CAST(foundp, NodeFTask)) {
|
||||
nodep->replaceWith(new AstMethodCall{nodep->fileline(),
|
||||
nodep->fromp()->unlinkFrBack(),
|
||||
nodep->name(), nullptr});
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return true;
|
||||
}
|
||||
UINFO(1, "found object " << foundp << endl);
|
||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< foundp->warnOther() << "... Location of found object\n"
|
||||
<< foundp->warnContextSecondary());
|
||||
}
|
||||
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr;
|
||||
}
|
||||
|
||||
VSpellCheck speller;
|
||||
for (AstClass* classp = first_classp; classp;) {
|
||||
for (AstNode* itemp = classp->membersp(); itemp; itemp = itemp->nextp()) {
|
||||
|
|
@ -2796,7 +2807,7 @@ private:
|
|||
"Member " << nodep->prettyNameQ() << " not found in class "
|
||||
<< first_classp->prettyNameQ() << "\n"
|
||||
<< (suggest.empty() ? "" : nodep->fileline()->warnMore() + suggest));
|
||||
return nullptr; // Caller handles error
|
||||
return false; // Caller handles error
|
||||
}
|
||||
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
|
||||
// Returns node if ok
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
#!/usr/bin/env perl
|
||||
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2022 by Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2023 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Foo;
|
||||
static int x = 1;
|
||||
endclass
|
||||
|
||||
class Bar;
|
||||
Foo f;
|
||||
function new;
|
||||
f = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class Baz;
|
||||
function static Bar get_bar;
|
||||
Bar b = new;
|
||||
return b;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class IntWrapper;
|
||||
int x;
|
||||
endclass
|
||||
|
||||
class Cls;
|
||||
static IntWrapper iw;
|
||||
function new;
|
||||
if (iw == null) iw = new;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
class ExtendCls extends Cls;
|
||||
endclass
|
||||
|
||||
class Getter1;
|
||||
function static int get_1;
|
||||
return 1;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
);
|
||||
|
||||
initial begin
|
||||
Foo foo = new;
|
||||
Bar bar = new;
|
||||
Baz baz = new;
|
||||
ExtendCls ec = new;
|
||||
Getter1 getter1 = new;
|
||||
|
||||
if (foo.x != 1) $stop;
|
||||
|
||||
foo.x = 2;
|
||||
if (foo.x != 2) $stop;
|
||||
|
||||
bar.f.x = 3;
|
||||
if (bar.f.x != 3) $stop;
|
||||
|
||||
baz.get_bar().f.x = 4;
|
||||
if (baz.get_bar().f.x != 4) $stop;
|
||||
|
||||
ec.iw.x = 5;
|
||||
if (ec.iw.x != 5) $stop;
|
||||
|
||||
if (getter1.get_1 != 1) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue