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:
parent
5712f9b614
commit
039c453164
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue