Fix type_id package scope resolution (#5826)

This commit is contained in:
Krzysztof Bieganski 2025-03-06 23:41:27 +01:00 committed by GitHub
parent 8e87a99628
commit b2093b513a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 605 additions and 1020 deletions

View File

@ -50,7 +50,8 @@ protected:
private:
// METHODS
const AstNodeDType* skipRefIterp(bool skipConst, bool skipEnum) const VL_MT_STABLE;
const AstNodeDType* skipRefIterp(bool skipConst, bool skipEnum,
bool assertOn = true) const VL_MT_STABLE;
protected:
// METHODS
@ -78,6 +79,15 @@ public:
return const_cast<AstNodeDType*>(
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, true));
}
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|ConstDType|EnumDType to other type,
// Returns null if not resolved
const AstNodeDType* skipRefOrNullp() const VL_MT_STABLE {
return skipRefIterp(true, true, false);
}
AstNodeDType* skipRefOrNullp() VL_MT_STABLE {
return const_cast<AstNodeDType*>(
static_cast<const AstNodeDType*>(this)->skipRefIterp(true, true, false));
}
// (Slow) Recurse over MemberDType|ParamTypeDType|RefDType|EnumDType to ConstDType
const AstNodeDType* skipRefToConstp() const { return skipRefIterp(false, true); }
AstNodeDType* skipRefToConstp() {

View File

@ -829,7 +829,8 @@ AstVar* AstVar::scVarRecurse(AstNode* nodep) {
return nullptr;
}
const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum) const VL_MT_STABLE {
const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum,
bool assertOn) const VL_MT_STABLE {
const AstNodeDType* nodep = this;
while (true) {
if (VL_UNLIKELY(VN_IS(nodep, MemberDType) || VN_IS(nodep, ParamTypeDType)
@ -840,7 +841,7 @@ const AstNodeDType* AstNodeDType::skipRefIterp(bool skipConst, bool skipEnum) co
nodep = subp;
continue;
} else {
nodep->v3fatalSrc(nodep->prettyTypeName() << " not linked to type");
if (assertOn) nodep->v3fatalSrc(nodep->prettyTypeName() << " not linked to type");
return nullptr;
}
}
@ -2669,7 +2670,9 @@ AstNodeModule* AstClassOrPackageRef::classOrPackageSkipp() const {
AstNode* lastp = nullptr;
while (foundp != lastp) {
lastp = foundp;
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) foundp = anodep->skipRefp();
if (AstNodeDType* const anodep = VN_CAST(foundp, NodeDType)) {
foundp = anodep->skipRefOrNullp();
}
if (AstTypedef* const anodep = VN_CAST(foundp, Typedef)) foundp = anodep->subDTypep();
if (AstClassRefDType* const anodep = VN_CAST(foundp, ClassRefDType))
foundp = anodep->classp();

View File

@ -765,10 +765,15 @@ public:
if (!foundp) baddot = dotname;
return foundp;
}
VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool classOnly,
const string& forWhat) {
VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool fallback,
bool classOnly, const string& forWhat) {
if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp());
VSymEnt* foundp = lookSymp->findIdFallback(nodep->name());
VSymEnt* foundp;
if (fallback) {
foundp = lookSymp->findIdFallback(nodep->name());
} else {
foundp = lookSymp->findIdFlat(nodep->name());
}
if (!foundp && v3Global.rootp()->stdPackagep()) { // Look under implied std::
foundp = getNodeSym(v3Global.rootp()->stdPackagep())->findIdFlat(nodep->name());
}
@ -1038,12 +1043,8 @@ class LinkDotFindVisitor final : public VNVisitor {
}
}
void visit(AstClassOrPackageRef* nodep) override {
if (!nodep->classOrPackageNodep()) {
if (nodep->name() == "$unit") {
nodep->classOrPackageNodep(v3Global.rootp()->dollarUnitPkgAddp());
} else if (nodep->name() == "std") {
nodep->classOrPackageNodep(v3Global.rootp()->stdPackagep());
}
if (!nodep->classOrPackageNodep() && nodep->name() == "$unit") {
nodep->classOrPackageNodep(v3Global.rootp()->dollarUnitPkgAddp());
}
iterateChildren(nodep);
}
@ -1183,7 +1184,7 @@ class LinkDotFindVisitor final : public VNVisitor {
= VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef);
if (dotp) {
AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef);
m_statep->resolveClassOrPackage(m_curSymp, lhsp, false,
m_statep->resolveClassOrPackage(m_curSymp, lhsp, true, false,
"External definition :: reference");
AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class);
if (!lhsclassp) {
@ -1192,7 +1193,7 @@ class LinkDotFindVisitor final : public VNVisitor {
m_curSymp = m_statep->getNodeSym(lhsclassp);
upSymp = m_curSymp;
AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef);
m_statep->resolveClassOrPackage(m_curSymp, rhsp, false,
m_statep->resolveClassOrPackage(m_curSymp, rhsp, true, false,
"External definition :: reference");
AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class);
if (!rhsclassp) {
@ -1211,7 +1212,7 @@ class LinkDotFindVisitor final : public VNVisitor {
}
} else if (cpackagerefp) {
if (!cpackagerefp->classOrPackageSkipp()) {
m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, false,
m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, true, false,
"External definition :: reference");
}
AstClass* const classp = VN_CAST(cpackagerefp->classOrPackageSkipp(), Class);
@ -1338,7 +1339,7 @@ class LinkDotFindVisitor final : public VNVisitor {
"Unsupported: extern constraint definition with class-in-class");
} else {
if (!cpackagerefp->classOrPackageSkipp()) {
m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, false,
m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, true, false,
"External definition :: reference");
}
AstClass* const classp = VN_CAST(cpackagerefp->classOrPackageSkipp(), Class);
@ -2255,6 +2256,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
bool m_insideClassExtParam = false; // Inside a class from m_extendsParam
bool m_explicitSuperNew = false; // Hit a "super.new" call inside a "new" function
std::map<AstNode*, AstPin*> m_usedPins; // Pin used in this cell, map to duplicate
std::map<std::string, AstNodeModule*> m_modulesToRevisit; // Modules to revisit a second time
AstNode* m_lastDeferredp = nullptr; // Last node which requested a revisit of its module
struct DotStates final {
DotPosition m_dotPos; // Scope part of dotted resolution
@ -2500,6 +2503,13 @@ class LinkDotResolveVisitor final : public VNVisitor {
symIterateNull(nodep, m_statep->getNodeSym(nodep));
}
// Marks the current module to be revisited after the initial AST iteration
void revisitLater(AstNode* deferredNodep) {
// Need to revisit entire module to build up all the necessary context
m_lastDeferredp = deferredNodep;
m_modulesToRevisit.insert(std::make_pair(m_modp->name(), m_modp));
}
void updateVarUse(AstVar* nodep) {
// Avoid dotted.PARAM false positive when in a parameter block
// that is if ()'ed off by same dotted name as another block
@ -2744,12 +2754,58 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
}
}
} else if (VN_IS(nodep->lhsp(), ClassOrPackageRef)
|| (VN_IS(nodep->lhsp(), Dot) && VN_AS(nodep->lhsp(), Dot)->colon())) {
} else if (AstClassOrPackageRef* const lhsp
= VN_CAST(nodep->lhsp(), ClassOrPackageRef)) {
// m_ds.m_dotText communicates the cell prefix between stages
m_ds.m_dotPos = DP_PACKAGE;
UINFO(8, indent() << "iter.lhs " << m_ds.ascii() << " " << nodep << endl);
iterateAndNextNull(lhsp);
if (!lhsp->classOrPackageSkipp() && lhsp->name() != "local::") {
revisitLater(nodep);
m_ds = lastStates;
// Resolve function args before bailing
if (AstNodeFTaskRef* const ftaskrefp = VN_CAST(nodep->rhsp(), NodeFTaskRef)) {
iterateAndNextNull(ftaskrefp->pinsp());
}
return;
}
m_ds.m_dotPos = DP_PACKAGE;
// nodep->lhsp() may be a new node
if (AstClassOrPackageRef* const classOrPackageRefp
= VN_CAST(nodep->lhsp(), ClassOrPackageRef)) {
if (AstNode* const classOrPackageNodep
= classOrPackageRefp->classOrPackageSkipp()) {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackageNodep);
}
}
UINFO(8, indent() << "iter.ldone " << m_ds.ascii() << " " << nodep << endl);
} else if (VN_IS(nodep->lhsp(), Dot) && VN_AS(nodep->lhsp(), Dot)->colon()) {
// m_ds.m_dotText communicates the cell prefix between stages
UINFO(8, indent() << "iter.lhs " << m_ds.ascii() << " " << nodep << endl);
m_ds.m_dotPos = DP_PACKAGE;
iterateAndNextNull(nodep->lhsp());
// nodep->lhsp() may be a new node
if (AstClassOrPackageRef* const crefp
= VN_CAST(nodep->lhsp(), ClassOrPackageRef)) {
if (!crefp->classOrPackageSkipp()) {
revisitLater(nodep);
m_ds = lastStates;
// Resolve function args before bailing
if (AstNodeFTaskRef* const ftaskrefp
= VN_CAST(nodep->rhsp(), NodeFTaskRef)) {
iterateAndNextNull(ftaskrefp->pinsp());
}
return;
}
}
if (m_lastDeferredp == nodep->lhsp()) {
// LHS got deferred, so this node won't be resolved. Defer it too
m_ds = lastStates;
// Resolve function args before bailing
if (AstNodeFTaskRef* const ftaskrefp = VN_CAST(nodep->rhsp(), NodeFTaskRef)) {
iterateAndNextNull(ftaskrefp->pinsp());
}
return;
}
UINFO(8, indent() << "iter.ldone " << m_ds.ascii() << " " << nodep << endl);
} else {
m_ds.m_dotPos = DP_FIRST;
@ -2932,10 +2988,15 @@ class LinkDotResolveVisitor final : public VNVisitor {
first = true;
} else if (!cpackagerefp->classOrPackageSkipp()) {
VSymEnt* const foundp = m_statep->resolveClassOrPackage(
m_ds.m_dotSymp, cpackagerefp, false, ":: reference");
m_ds.m_dotSymp, cpackagerefp, true, false, ":: reference");
if (!foundp) return;
classOrPackagep = cpackagerefp->classOrPackageSkipp();
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
if (classOrPackagep) {
m_ds.m_dotSymp = m_statep->getNodeSym(classOrPackagep);
} else {
m_ds = lastStates;
return;
}
} else {
classOrPackagep = cpackagerefp->classOrPackageSkipp();
UASSERT_OBJ(classOrPackagep, m_ds.m_dotp->lhsp(), "Bad package link");
@ -3293,12 +3354,15 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_RESTORER(m_pinSymp);
if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") {
m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, false, ":: reference");
m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE,
false, ":: reference");
}
// ClassRef's have pins, so track
if (nodep->classOrPackageSkipp()) {
m_pinSymp = m_statep->getNodeSym(nodep->classOrPackageSkipp());
} else if (nodep->name() != "local::") {
return;
}
AstClass* const refClassp = VN_CAST(nodep->classOrPackageSkipp(), Class);
// Make sure any extends() are properly imported within referenced class
@ -3608,7 +3672,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
first = true;
} else if (!cpackagerefp->classOrPackageSkipp()) {
VSymEnt* const foundp = m_statep->resolveClassOrPackage(
m_ds.m_dotSymp, cpackagerefp, false, ":: reference");
m_ds.m_dotSymp, cpackagerefp, true, false, ":: reference");
if (foundp) nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
} else {
nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
@ -3968,7 +4032,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
iterate(lookNodep);
cprp = dotp->rhsp();
VSymEnt* const foundp = m_statep->resolveClassOrPackage(
lookSymp, lookNodep, false, nodep->verilogKwd());
lookSymp, lookNodep, true, false, nodep->verilogKwd());
if (!foundp) return;
UASSERT_OBJ(lookNodep->classOrPackageSkipp(), nodep, "Bad package link");
lookSymp = m_statep->getNodeSym(lookNodep->classOrPackageSkipp());
@ -3984,7 +4048,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
return;
}
VSymEnt* const foundp = m_statep->resolveClassOrPackage(lookSymp, cpackagerefp, true,
nodep->verilogKwd());
true, nodep->verilogKwd());
if (foundp) {
if (AstClass* const classp = VN_CAST(foundp->nodep(), Class)) {
AstPin* paramsp = cpackagerefp->paramsp();
@ -4151,7 +4215,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
if (!cpackagerefp->classOrPackageSkipp()) {
VSymEnt* const foundp = m_statep->resolveClassOrPackage(
m_ds.m_dotSymp, cpackagerefp, false, "class/package reference");
m_ds.m_dotSymp, cpackagerefp, true, false, "class/package reference");
if (!foundp) return;
}
nodep->classOrPackagep(cpackagerefp->classOrPackageSkipp());
@ -4327,6 +4391,13 @@ public:
: m_statep{statep} {
UINFO(4, __FUNCTION__ << ": " << endl);
iterate(rootp);
std::map<std::string, AstNodeModule*> modulesToRevisit = std::move(m_modulesToRevisit);
m_lastDeferredp = nullptr;
for (auto& p : modulesToRevisit) {
AstNodeModule* const modp = p.second;
modp->foreach([](AstNode* const nodep) { nodep->user3(false); });
iterate(modp);
}
}
~LinkDotResolveVisitor() override = default;
};

View File

@ -712,7 +712,7 @@ void V3ParseImp::tokenPipelineSym() {
} else { // Not found
yylval.scp = nullptr;
if (token == yaID__CC) {
if (!v3Global.opt.bboxUnsup()) {
if (!m_afterColonColon & !v3Global.opt.bboxUnsup()) {
// IEEE does require this, but we may relax this as UVM breaks it, so allow
// bbox for today
// We'll get a parser error eventually but might not be obvious

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,36 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
typedef class Bar;
typedef Bar Baz;
module t;
initial begin
Bar::Qux::boo(1);
Baz::Qux::boo(1);
if (!Bar::Qux::finish) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule
class Foo #(type T);
static logic finish = 0;
static function void boo(input logic rec);
if (rec) Bar::Qux::boo(0);
finish = 1;
endfunction
endclass
class Goo #(type T);
function void goo();
T::Qux::boo(1);
endfunction
endclass
class Bar;
typedef Foo#(Bar) Qux;
endclass

View File

@ -1,7 +1,4 @@
%Error: t/t_interface_colon_bad.v:14:7: Package/class for ':: reference' not found: 'iface'
14 | iface::func();
| ^~~~~
%Error: t/t_interface_colon_bad.v:14:14: Can't find definition of task/function: 'func'
14 | iface::func();
| ^~~~
%Error: Exiting due to

View File

@ -1,5 +1,4 @@
%Error-PKGNODECL: t/t_package_identifier_bad.v:15:20: Package/class 'Bar' not found, and needs to be predeclared (IEEE 1800-2023 26.3)
%Error: t/t_package_identifier_bad.v:15:20: Package/class for ':: reference' not found: 'Bar'
15 | int baz = Foo::Bar::baz;
| ^~~
... For error description see https://verilator.org/warn/PKGNODECL?v=latest
%Error: Exiting due to

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2024 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
import vltest_bootstrap
test.scenarios('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,31 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2025 by Antmicro.
// SPDX-License-Identifier: CC0-1.0
class Foo;
static function int get(int x);
return x;
endfunction
endclass
class Bar;
static function int get;
return 42;
endfunction
endclass
class Qux #(type Tfoo, type Tbar);
static function int get();
return Tfoo::get(Tbar::get());
endfunction
endclass
module t;
initial begin
if (Qux#(Foo, Bar)::get() != 42) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -11,6 +11,6 @@ import vltest_bootstrap
test.scenarios('linter')
test.lint(verilator_flags2=["-DTEST_DECLARE_STD"], fails=test.vlt_all) # Issue #4705 due to ::
test.lint(verilator_flags2=["-DTEST_DECLARE_STD"])
test.passes()

View File

@ -1,5 +1,7 @@
%Error-PKGNODECL: t/t_std_identifier.v:16:20: Package/class 'std' not found, and needs to be predeclared (IEEE 1800-2023 26.3)
%Error: t/t_std_identifier.v:16:20: Package/class for ':: reference' not found: 'std'
16 | int baz = foo::std::bar;
| ^~~
... For error description see https://verilator.org/warn/PKGNODECL?v=latest
%Error: t/t_std_identifier.v:16:25: Can't find definition of scope/variable/func: 'bar'
16 | int baz = foo::std::bar;
| ^~~
%Error: Exiting due to

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff