From ed7040adc03a77147381bd89ae82d69d454dcfb0 Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Thu, 22 Aug 2024 21:49:08 +0200 Subject: [PATCH] Fix dot fallback finding wrong symbols (#5394) --- src/V3LinkDot.cpp | 117 ++++++++++++++++++------------ test_regress/t/t_fallback_bad.out | 30 ++++++++ test_regress/t/t_fallback_bad.pl | 19 +++++ test_regress/t/t_fallback_bad.v | 43 +++++++++++ 4 files changed, 164 insertions(+), 45 deletions(-) create mode 100644 test_regress/t/t_fallback_bad.out create mode 100755 test_regress/t/t_fallback_bad.pl create mode 100644 test_regress/t/t_fallback_bad.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 0cbf72f68..80f47a9fc 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -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); diff --git a/test_regress/t/t_fallback_bad.out b/test_regress/t/t_fallback_bad.out new file mode 100644 index 000000000..31a449042 --- /dev/null +++ b/test_regress/t/t_fallback_bad.out @@ -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': +%Error: Exiting due to diff --git a/test_regress/t/t_fallback_bad.pl b/test_regress/t/t_fallback_bad.pl new file mode 100755 index 000000000..a60503a1f --- /dev/null +++ b/test_regress/t/t_fallback_bad.pl @@ -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; diff --git a/test_regress/t/t_fallback_bad.v b/test_regress/t/t_fallback_bad.v new file mode 100644 index 000000000..522f0c565 --- /dev/null +++ b/test_regress/t/t_fallback_bad.v @@ -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