Fix dot fallback finding wrong symbols (#5394)

This commit is contained in:
Arkadiusz Kozdra 2024-08-22 21:49:08 +02:00 committed by GitHub
parent 48776277bd
commit ed7040adc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 164 additions and 45 deletions

View File

@ -577,13 +577,22 @@ private:
return findp;
}
VSymEnt* findWithAltFlat(VSymEnt* symp, const string& name, const string& altname) {
VSymEnt* findp = symp->findIdFlat(name);
if (findp) return findp;
if (altname != "") {
UINFO(8, " alt flat\n");
findp = symp->findIdFlat(altname);
}
return findp;
}
public:
VSymEnt* findDotted(FileLine* refLocationp, VSymEnt* lookupSymp, const string& dotname,
string& baddot, VSymEnt*& okSymp) {
string& baddot, VSymEnt*& okSymp, bool firstId) {
// Given a dotted hierarchy name, return where in scope it is
// Note when dotname=="" we just fall through and return lookupSymp
UINFO(8, " dottedFind se" << cvtToHex(lookupSymp) << " '" << dotname << "'" << endl);
bool firstId = true;
string leftname = dotname;
okSymp = lookupSymp; // So can list bad scopes
while (leftname != "") { // foreach dotted part of xref name
@ -666,7 +675,7 @@ public:
if (!lookupSymp) return nullptr; // Not found
}
} else { // Searching for middle submodule, must be a cell name
if (VSymEnt* const findSymp = findWithAltFallback(lookupSymp, ident, altIdent)) {
if (VSymEnt* const findSymp = findWithAltFlat(lookupSymp, ident, altIdent)) {
lookupSymp = findSymp;
} else {
return nullptr; // Not found
@ -701,7 +710,8 @@ public:
}
}
VSymEnt* findSymPrefixed(VSymEnt* lookupSymp, const string& dotname, string& baddot) {
VSymEnt* findSymPrefixed(VSymEnt* lookupSymp, const string& dotname, string& baddot,
bool fallback) {
// Find symbol in given point in hierarchy, allowing prefix (post-Inline)
// For simplicity lookupSymp may be passed nullptr result from findDotted
if (!lookupSymp) return nullptr;
@ -709,11 +719,15 @@ public:
<< dotname << " under se" << cvtToHex(lookupSymp)
<< ((lookupSymp->symPrefix() == "") ? "" : " as ")
<< ((lookupSymp->symPrefix() == "") ? "" : lookupSymp->symPrefix() + dotname)
<< " at se" << lookupSymp << endl);
<< " at se" << lookupSymp << " fallback=" << fallback << endl);
string prefix = lookupSymp->symPrefix();
VSymEnt* foundp = nullptr;
while (!foundp) {
foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be nullptr
if (fallback) {
foundp = lookupSymp->findIdFallback(prefix + dotname); // Might be nullptr
} else {
foundp = lookupSymp->findIdFlat(prefix + dotname); // Might be nullptr
}
if (prefix.empty()) break;
const string nextPrefix = removeLastInlineScope(prefix);
if (prefix == nextPrefix) break;
@ -1000,7 +1014,8 @@ class LinkDotFindVisitor final : public VNVisitor {
const string scope = origname.substr(0, pos);
string baddot;
VSymEnt* okSymp;
aboveSymp = m_statep->findDotted(nodep->fileline(), aboveSymp, scope, baddot, okSymp);
aboveSymp
= m_statep->findDotted(nodep->fileline(), aboveSymp, scope, baddot, okSymp, false);
UASSERT_OBJ(aboveSymp, nodep,
"Can't find instance insertion point at "
<< AstNode::prettyNameQ(baddot) << " in: " << nodep->prettyNameQ());
@ -1025,7 +1040,8 @@ class LinkDotFindVisitor final : public VNVisitor {
const string ident = dottedname.substr(pos + std::strlen("__DOT__"));
string baddot;
VSymEnt* okSymp;
aboveSymp = m_statep->findDotted(nodep->fileline(), aboveSymp, dotted, baddot, okSymp);
aboveSymp = m_statep->findDotted(nodep->fileline(), aboveSymp, dotted, baddot, okSymp,
false);
UASSERT_OBJ(aboveSymp, nodep,
"Can't find cellinline insertion point at "
<< AstNode::prettyNameQ(baddot) << " in: " << nodep->prettyNameQ());
@ -1857,15 +1873,15 @@ class LinkDotScopeVisitor final : public VNVisitor {
string baddot;
VSymEnt* okSymp;
VSymEnt* cellSymp = m_statep->findDotted(nodep->fileline(), m_modSymp, ifcellname,
baddot, okSymp);
baddot, okSymp, false);
UASSERT_OBJ(
cellSymp, nodep,
"No symbol for interface instance: " << nodep->prettyNameQ(ifcellname));
UINFO(5, " Found interface instance: se" << cvtToHex(cellSymp) << " "
<< cellSymp->nodep() << endl);
if (dtypep->modportName() != "") {
VSymEnt* const mpSymp = m_statep->findDotted(nodep->fileline(), m_modSymp,
ifcellname, baddot, okSymp);
VSymEnt* const mpSymp = m_statep->findDotted(
nodep->fileline(), m_modSymp, ifcellname, baddot, okSymp, false);
UASSERT_OBJ(mpSymp, nodep,
"No symbol for interface modport: "
<< nodep->prettyNameQ(dtypep->modportName()));
@ -1926,7 +1942,7 @@ class LinkDotScopeVisitor final : public VNVisitor {
string baddot;
VSymEnt* okSymp;
symp = m_statep->findDotted(nodep->rhsp()->fileline(), m_modSymp, scopename,
baddot, okSymp);
baddot, okSymp, false);
if (inl == "") break;
inl = LinkDotState::removeLastInlineScope(inl);
}
@ -1952,7 +1968,7 @@ class LinkDotScopeVisitor final : public VNVisitor {
string baddot;
VSymEnt* okSymp;
VSymEnt* const symp = m_statep->findDotted(nodep->lhsp()->fileline(), m_modSymp,
scopename, baddot, okSymp);
scopename, baddot, okSymp, false);
UASSERT_OBJ(symp, nodep, "No symbol for interface alias lhs");
UINFO(5, " Found a linked scope LHS: " << scopename << " se" << cvtToHex(symp)
<< " " << symp->nodep() << endl);
@ -2085,7 +2101,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
enum DotPosition : uint8_t {
DP_NONE = 0, // Not under a DOT
DP_PACKAGE, // {package}:: DOT
DP_SCOPE, // [DOT...] {scope-or-var} DOT
DP_FIRST, // {scope-or-var} DOT
DP_SCOPE, // DOT... {scope-or-var} DOT
DP_FINAL, // [DOT...] {var-or-func-or-dtype} with no following dots
DP_MEMBER
}; // DOT {member-name} [DOT...]
@ -2524,7 +2541,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
m_ds.init(m_curSymp); // Start from current point
}
m_ds.m_dotp = nodep; // Always, not just at start
m_ds.m_dotPos = DP_SCOPE;
m_ds.m_dotPos = DP_FIRST;
if (VN_IS(nodep->lhsp(), ParseRef) && nodep->lhsp()->name() == "this") {
VSymEnt* classSymp = getThisClassSymp();
@ -2566,7 +2583,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
m_ds.m_dotPos = DP_PACKAGE;
iterateAndNextNull(nodep->lhsp());
} else {
m_ds.m_dotPos = DP_SCOPE;
m_ds.m_dotPos = DP_FIRST;
iterateAndNextNull(nodep->lhsp());
// if (debug() >= 9) nodep->dumpTree("- dot-lho: ");
}
@ -2578,6 +2595,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
&& (VN_IS(nodep->lhsp(), CellRef) || VN_IS(nodep->lhsp(), CellArrayRef))) {
m_ds.m_unlinkedScopep = nodep->lhsp();
}
if (m_ds.m_dotPos == DP_FIRST) m_ds.m_dotPos = DP_SCOPE;
if (!m_ds.m_dotErr) { // Once something wrong, give up
// Top 'final' dot RHS is final RHS, else it's a
// DOT(DOT(x,*here*),real-rhs) which we consider a RHS
@ -2633,6 +2651,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
"ParseRefs should no longer exist");
const DotStates lastStates = m_ds;
const bool start = (m_ds.m_dotPos == DP_NONE); // Save, as m_dotp will be changed
bool first = start || m_ds.m_dotPos == DP_FIRST;
if (start) {
m_ds.init(m_curSymp);
@ -2703,11 +2722,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
if (cpackagerefp->name() == "local::") {
m_fromSymp = nullptr;
first = true;
} else {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
}
m_ds.m_dotPos = DP_SCOPE;
} else if (m_ds.m_dotPos == DP_SCOPE) {
} else if (m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST) {
// {a}.{b}, where {a} maybe a module name
// or variable, where dotting into structure member
expectWhat = "scope/variable";
@ -2727,6 +2747,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
if (m_fromSymp) {
foundp = m_fromSymp->findIdFlat(nodep->name());
if (foundp) {
if (!start) m_ds.m_dotPos = DP_MEMBER;
if (!m_inWith) {
UASSERT_OBJ(m_randMethodCallp, nodep, "Expected to be under randomize()");
// This will start failing once complex expressions are allowed on the LHS
@ -2735,15 +2756,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
"Expected simple randomize target");
// A ParseRef is used here so that the dot RHS gets resolved
nodep->replaceWith(new AstMemberSel{
nodep->fileline(),
new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT,
m_randMethodCallp->fromp()->name()},
nodep->fileline(), m_randMethodCallp->fromp()->cloneTree(false),
VFlagChildDType{}, nodep->name()});
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
}
UINFO(9, " randomize-with fromSym " << foundp->nodep() << endl);
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
AstLambdaArgRef* const lambdaRefp
= new AstLambdaArgRef{nodep->fileline(), "item", false};
nodep->replaceWith(new AstMemberSel{nodep->fileline(), lambdaRefp,
@ -2754,9 +2772,11 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
if (allowScope) {
foundp = m_statep->findDotted(nodep->fileline(), m_ds.m_dotSymp, nodep->name(),
baddot, okSymp); // Maybe nullptr
} else {
baddot, okSymp, first); // Maybe nullptr
} else if (first) {
foundp = m_ds.m_dotSymp->findIdFallback(nodep->name());
} else {
foundp = m_ds.m_dotSymp->findIdFlat(nodep->name());
}
if (foundp) {
UINFO(9, " found=se" << cvtToHex(foundp) << " exp=" << expectWhat
@ -2765,7 +2785,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
// What fell out?
bool ok = false;
// Special case: waiting on clocking event
if (m_inSens && foundp && m_ds.m_dotPos != DP_SCOPE) {
if (m_inSens && foundp && m_ds.m_dotPos != DP_SCOPE && m_ds.m_dotPos != DP_FIRST) {
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
foundp = getCreateClockingEventSymEnt(clockingp);
}
@ -2815,7 +2835,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
nodep->replaceWith(taskrefp);
VL_DO_DANGLING(nodep->deleteTree(), nodep);
if (start) m_ds = lastStates;
m_ds = lastStates;
return;
} else if (AstVar* const varp = foundToVarp(foundp, nodep, VAccess::READ)) {
AstIfaceRefDType* const ifacerefp
@ -2961,7 +2981,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
} else if (VN_IS(foundp->nodep(), Clocking)) {
m_ds.m_dotSymp = foundp;
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
ok = m_ds.m_dotPos == DP_SCOPE;
ok = m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST;
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
if (!ftaskp->isFunction() || ftaskp->classMethod()) {
@ -2986,7 +3006,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
// Mark that the Dot statement can't be resolved.
m_ds.m_unresolvedClass = true;
// If the symbol was a scope name, it would be resolved.
if (m_ds.m_dotPos == DP_SCOPE) m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotPos = DP_MEMBER;
} else {
// Cells/interfaces can't be implicit
const bool isCell = foundp ? VN_IS(foundp->nodep(), Cell) : false;
@ -3128,15 +3148,16 @@ class LinkDotResolveVisitor final : public VNVisitor {
// ignore (t_math_divw)
dotSymp = m_modSymp;
const string inl = AstNode::dedotName(nodep->inlinedDots());
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, inl, baddot, okSymp);
dotSymp
= m_statep->findDotted(nodep->fileline(), dotSymp, inl, baddot, okSymp, false);
UASSERT_OBJ(dotSymp, nodep,
"Couldn't resolve inlined scope " << AstNode::prettyNameQ(baddot)
<< " in: " << nodep->inlinedDots());
}
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
okSymp); // Maybe nullptr
okSymp, true); // Maybe nullptr
if (!m_statep->forScopeCreation()) {
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
VSymEnt* foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true);
if (m_inSens && foundp) {
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
foundp = getCreateClockingEventSymEnt(clockingp);
@ -3169,7 +3190,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
}
} else {
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
VSymEnt* const foundp
= m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, true);
AstVarScope* vscp = foundp ? VN_AS(foundp->nodep(), VarScope) : nullptr;
if (!vscp) {
nodep->v3error("Can't find varpin scope of "
@ -3290,6 +3312,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_RESTORER(m_fromSymp);
bool first = !m_ds.m_dotp || m_ds.m_dotPos == DP_FIRST;
bool staticAccess = false;
if (m_ds.m_unresolvedClass) {
// Unable to link before V3Param
@ -3312,12 +3335,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
if (cpackagerefp->name() == "local::") {
m_fromSymp = nullptr;
first = true;
} else {
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
}
// Class/package :: HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotp = nullptr;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_FINAL) {
nodep->dotted(m_ds.m_dotText); // Maybe ""
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_MEMBER) {
@ -3331,7 +3354,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
return;
} else if (m_ds.m_dotp && m_ds.m_dotPos == DP_SCOPE) {
} else if (m_ds.m_dotp && (m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST)) {
// HERE function() . method_called_on_function_return_value()
m_ds.m_dotPos = DP_MEMBER;
m_ds.m_dotText = "";
@ -3371,8 +3394,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
dotSymp = m_modSymp;
const string inl = AstNode::dedotName(nodep->inlinedDots());
UINFO(8, " Inlined " << inl << endl);
dotSymp
= m_statep->findDotted(nodep->fileline(), dotSymp, inl, baddot, okSymp);
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, inl, baddot, okSymp,
true);
if (!dotSymp) {
okSymp->cellErrorScopes(nodep);
nodep->v3fatalSrc("Couldn't resolve inlined scope "
@ -3381,7 +3404,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
}
dotSymp = m_statep->findDotted(nodep->fileline(), dotSymp, nodep->dotted(), baddot,
okSymp); // Maybe nullptr
okSymp, true); // Maybe nullptr
}
if (m_fromSymp) {
VSymEnt* const foundp = m_fromSymp->findIdFlat(nodep->name());
@ -3401,7 +3424,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
return;
}
}
VSymEnt* const foundp = m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot);
VSymEnt* const foundp
= m_statep->findSymPrefixed(dotSymp, nodep->name(), baddot, first);
AstNodeFTask* const taskp
= foundp ? VN_CAST(foundp->nodep(), NodeFTask) : nullptr; // Maybe nullptr
if (taskp) {
@ -3506,12 +3530,13 @@ class LinkDotResolveVisitor final : public VNVisitor {
if (nodep->user3SetOnce()) return;
iterateAndNextNull(nodep->fromp());
if (m_ds.m_unresolvedClass) {
UASSERT_OBJ(m_ds.m_dotPos != DP_SCOPE, nodep,
UASSERT_OBJ(m_ds.m_dotPos != DP_SCOPE && m_ds.m_dotPos != DP_FIRST, nodep,
"Object of unresolved class on scope position in dotted reference");
return;
}
if (m_ds.m_dotPos
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
if (m_ds.m_dotPos == DP_SCOPE
|| m_ds.m_dotPos
== DP_FIRST) { // Already under dot, so this is {modulepart} DOT {modulepart}
UINFO(9, " deferring until after a V3Param pass: " << nodep << endl);
m_ds.m_dotText += "__BRA__??__KET__";
m_ds.m_unresolvedCell = true;
@ -3527,7 +3552,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
iterateAndNextNull(nodep->attrp());
}
}
if (m_ds.m_unresolvedCell && m_ds.m_dotPos == DP_SCOPE) {
if (m_ds.m_unresolvedCell && (m_ds.m_dotPos == DP_SCOPE || m_ds.m_dotPos == DP_FIRST)) {
AstNodeExpr* const exprp = nodep->bitp()->unlinkFrBack();
AstCellArrayRef* const newp
= new AstCellArrayRef{nodep->fileline(), nodep->fromp()->name(), exprp};
@ -3538,8 +3563,9 @@ class LinkDotResolveVisitor final : public VNVisitor {
void visit(AstNodePreSel* nodep) override {
// Excludes simple AstSelBit, see above
if (nodep->user3SetOnce()) return;
if (m_ds.m_dotPos
== DP_SCOPE) { // Already under dot, so this is {modulepart} DOT {modulepart}
if (m_ds.m_dotPos == DP_SCOPE
|| m_ds.m_dotPos
== DP_FIRST) { // Already under dot, so this is {modulepart} DOT {modulepart}
nodep->v3error("Syntax error: Range ':', '+:' etc are not allowed in the instance "
"part of a dotted reference");
m_ds.m_dotErr = true;
@ -3861,7 +3887,6 @@ class LinkDotResolveVisitor final : public VNVisitor {
UASSERT_OBJ(cpackagerefp->classOrPackagep(), m_ds.m_dotp->lhsp(), "Bad package link");
nodep->classOrPackagep(cpackagerefp->classOrPackagep());
m_ds.m_dotPos = DP_SCOPE;
m_ds.m_dotp = nullptr;
} else {
checkNoDot(nodep);
}
@ -3870,8 +3895,10 @@ class LinkDotResolveVisitor final : public VNVisitor {
const VSymEnt* foundp;
if (nodep->classOrPackagep()) {
foundp = m_statep->getNodeSym(nodep->classOrPackagep())->findIdFlat(nodep->name());
} else {
} else if (m_ds.m_dotPos == DP_FIRST || m_ds.m_dotPos == DP_NONE) {
foundp = m_curSymp->findIdFallback(nodep->name());
} else {
foundp = m_curSymp->findIdFlat(nodep->name());
}
if (AstTypedef* const defp = foundp ? VN_CAST(foundp->nodep(), Typedef) : nullptr) {
nodep->typedefp(defp);

View File

@ -0,0 +1,30 @@
%Error: t/t_fallback_bad.v:26:16: Can't find definition of task/function: 'tsk'
26 | super.tsk;
| ^~~
%Error: t/t_fallback_bad.v:27:15: Can't find definition of task/function: 'tsk'
27 | this.tsk;
| ^~~
%Error: t/t_fallback_bad.v:28:16: Can't find definition of variable: 'f'
28 | super.f = 8;
| ^
%Error: t/t_fallback_bad.v:29:15: Can't find definition of variable: 'f'
29 | this.f = 8;
| ^
%Error: t/t_fallback_bad.v:30:20: Can't find definition of 'tsk' in dotted task/function: 'sub1.sub2.tsk'
30 | sub1.sub2.tsk;
| ^~~
... Known scopes under 'tsk': sub2
%Error: t/t_fallback_bad.v:31:15: Can't find definition of scope/variable/func: 'f'
31 | pkg::f = 8;
| ^
%Error: t/t_fallback_bad.v:32:15: Can't find definition of task/function: 'tsk'
32 | pkg::tsk();
| ^~~
%Error: t/t_fallback_bad.v:33:20: Can't find definition of 'f' in dotted variable: 'sub1.sub2.f'
33 | sub1.sub2.f = 8;
| ^
%Error: t/t_fallback_bad.v:34:20: Can't find definition of 'f' in dotted scope/variable: 'sub1.sub2.f'
34 | sub1.sub2.f.f = 8;
| ^
... Known scopes under 'sub1.sub2': <no instances found>
%Error: Exiting due to

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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(linter => 1);
lint(
fails => 1,
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,43 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2024 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
int f = 5;
task tsk; endtask
package pkg;
endpackage
module subm;
endmodule
module submo;
subm sub2;
endmodule
module t;
submo sub1;
class Base;endclass
class Cls extends Base;
task calltsk;
super.tsk;
this.tsk;
super.f = 8;
this.f = 8;
sub1.sub2.tsk;
pkg::f = 8;
pkg::tsk();
sub1.sub2.f = 8;
sub1.sub2.f.f = 8;
endtask
endclass
Cls obj = new;
initial begin
obj.calltsk;
if (f != 5) $stop;
end
endmodule