Fix class scope '::' reference through an inherited type parameter

Resolving a '::' class-scope reference whose left-hand side is a type
parameter inherited from a parameterized base class failed with
"Package/class for ':: reference' not found", for example a
non-parameterized sequence extending uvm_sequence #(my_item) that uses
REQ::type_id::create().

Such a reference is only resolvable once V3Param binds the inherited
type parameter, so when inside a class that extends a parameterized
base, defer the reference during the primary link pass instead of
erroring; a genuinely unknown name is still reported in the
post-V3Param pass. Also accept the post-V3Param resolved parameter
type in checkIfClassOrPackage(), where the declared child has been
consumed and the bound type is the resolved data type.

Disclosure: developed with the assistance of an AI agent; reviewed,
verified, and tested by the human contributor.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: serge06 <chusserge@mail.ru>
This commit is contained in:
serge06 2026-06-19 16:47:58 +03:00
parent 5712f9b614
commit 039c453164
4 changed files with 88 additions and 5 deletions

View File

@ -256,6 +256,7 @@ Samuel Riedel
Sean Cross Sean Cross
Sebastien Van Cauwenberghe Sebastien Van Cauwenberghe
Secturion Secturion
Sergey Chusov
Sergey Fedorov Sergey Fedorov
Sergi Granell Sergi Granell
Seth Pellegrino Seth Pellegrino

View File

@ -1013,18 +1013,23 @@ public:
if (checkUnresolvedRef(VN_CAST(dtypep, RefDType))) return true; if (checkUnresolvedRef(VN_CAST(dtypep, RefDType))) return true;
} else if (const AstParamTypeDType* const paramTypep } else if (const AstParamTypeDType* const paramTypep
= VN_CAST(symp->nodep(), ParamTypeDType)) { = VN_CAST(symp->nodep(), ParamTypeDType)) {
// ParamTypeDType child may be wrapped in RequireDType or unwrapped // Before V3Param the declared default is in childDTypep (possibly
// wrapped in a RequireDType); after V3Param it is consumed and the
// bound type is the resolved data type, e.g. a type parameter
// inherited from a specialized base class (REQ #(Item) -> class Item).
AstNode* childp = paramTypep->childDTypep(); AstNode* childp = paramTypep->childDTypep();
if (const AstRequireDType* const reqp = VN_CAST(childp, RequireDType)) { if (const AstRequireDType* const reqp = VN_CAST(childp, RequireDType)) {
childp = reqp->lhsp(); childp = reqp->lhsp();
} }
if (isValidTypeNode(childp)) return true; const AstNode* const checkp = childp ? childp : paramTypep->skipRefp();
if (checkUnresolvedRef(VN_CAST(childp, RefDType))) return true; if (isValidTypeNode(checkp)) return true;
if (checkUnresolvedRef(VN_CAST(checkp, RefDType))) return true;
} }
return false; return false;
} }
VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool fallback, VSymEnt* resolveClassOrPackage(VSymEnt* lookSymp, AstClassOrPackageRef* nodep, bool fallback,
bool classOnly, const string& forWhat) { bool classOnly, const string& forWhat,
bool deferIfUnresolved = false) {
if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp()); if (nodep->classOrPackageSkipp()) return getNodeSym(nodep->classOrPackageSkipp());
VSymEnt* foundp; VSymEnt* foundp;
VSymEnt* searchSymp = lookSymp; VSymEnt* searchSymp = lookSymp;
@ -1050,6 +1055,14 @@ public:
nodep->classOrPackageNodep(foundp->nodep()); nodep->classOrPackageNodep(foundp->nodep());
return foundp; return foundp;
} }
if (deferIfUnresolved) {
// Inside a class that extends a parameterized base, the name may be a
// type parameter (or other member) inherited from that base, which is
// only resolvable once the base is deparameterized by V3Param. Leave the
// reference unresolved so the caller defers it; a genuine bad name will
// still error in the post-V3Param link pass.
return nullptr;
}
const string suggest const string suggest
= suggestSymFallback(lookSymp, nodep->name(), LinkNodeMatcherClassOrPackage{}); = suggestSymFallback(lookSymp, nodep->name(), LinkNodeMatcherClassOrPackage{});
nodep->v3error((classOnly ? "Class" : "Package/class") nodep->v3error((classOnly ? "Class" : "Package/class")
@ -4752,8 +4765,12 @@ class LinkDotResolveVisitor final : public VNVisitor {
VL_RESTORER(m_pinSymp); VL_RESTORER(m_pinSymp);
if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") { if (!nodep->classOrPackageSkipp() && nodep->name() != "local::") {
// When inside a class that extends a parameterized base, an
// unresolved name may be a base-class type parameter that only
// becomes available after V3Param; defer rather than error.
const bool deferIfUnresolved = m_statep->forPrimary() && m_insideClassExtParam;
m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE, m_statep->resolveClassOrPackage(m_ds.m_dotSymp, nodep, m_ds.m_dotPos != DP_PACKAGE,
false, ":: reference"); false, ":: reference", deferIfUnresolved);
} }
// ClassRef's have pins, so track // ClassRef's have pins, so track

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2024 Wilson Snyder
# 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,47 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Sergey Chusov
// SPDX-License-Identifier: CC0-1.0
// A factory-like static method, as used by UVM's type_id::create().
class registry #(type T = int);
static function T create();
T r = new;
return r;
endfunction
endclass
class item;
int val;
typedef registry#(item) type_id;
function new();
val = 42;
endfunction
endclass
class seq_base #(type REQ = int, type RSP = REQ);
REQ req;
endclass
// Non-parameterized class extending a parameterized base.
// REQ is a type parameter inherited from the (specialized) base class and is
// here used as a '::' class scope: REQ::type_id::create(). Resolving REQ in
// this position requires deferring until after V3Param has bound REQ to item.
class seq extends seq_base #(item);
function int make();
REQ r;
r = REQ::type_id::create();
return r.val;
endfunction
endclass
module t;
seq s;
initial begin
s = new;
if (s.make() != 42) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule