Fix selects of static members (#4326)

This commit is contained in:
Ryszard Rozak 2023-06-30 09:12:22 +02:00 committed by GitHub
parent 32019d2bc4
commit 242f661644
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 155 additions and 30 deletions

View File

@ -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; }

View File

@ -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 << " -> ";

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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