Fix resolving default/nondefault type-of-type parameters (#7380) (#7385)

This commit is contained in:
em2machine 2026-04-07 17:58:36 -04:00 committed by GitHub
parent 8c11d0d0bd
commit 2736262b98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 128 additions and 17 deletions

View File

@ -1235,30 +1235,29 @@ class ParamProcessor final {
}
}
// Check if exprp is a ClassRefDType whose class is the default-parameter clone
// of origp's template class. This catches the case where an explicit type parameter
// like Holder#(W#(int)) resolves to the same specialization as the implicit default
// Holder#(), because W#(int) deparameterizes to W_ (the all-default clone of W).
// Uses the user4p link set at line ~1658 when defaultsResolved is true.
static bool classTypeMatchesDefaultClone(const AstNodeDType* exprp,
const AstNodeDType* origp) {
// Check if exprp's class matches origp's class after deparameterization.
// Handles both the simple case (user4p link from defaultsResolved) and the
// nested case where the default's inner class has non-default sub-parameters
// (e.g., uvm_sequence#(uvm_reg_item) where uvm_reg_item != default uvm_sequence_item).
bool classTypeMatchesDefaultClone(const AstNodeDType* exprp, const AstNodeDType* origp) {
exprp = exprp->skipRefp();
origp = origp->skipRefp();
const auto* const exprClassRefp = VN_CAST(exprp, ClassRefDType);
const auto* const origClassRefp = VN_CAST(origp, ClassRefDType);
UINFO(9, "classTypeMatchesDefaultClone: exprClassRef="
<< exprClassRefp << " origClassRef=" << origClassRefp);
if (!exprClassRefp || !origClassRefp) return false;
// Fast path: check user4p link (set when template was deparameterized with defaults)
const AstNodeModule* const defaultClonep
= VN_CAST(origClassRefp->classp()->user4p(), Class);
const bool result = defaultClonep && defaultClonep == exprClassRefp->classp();
UINFO(9, " origClass=" << origClassRefp->classp()->prettyNameQ()
<< " origClassp=" << cvtToHex(origClassRefp->classp())
<< " user4p=" << (defaultClonep ? cvtToHex(defaultClonep) : "null")
<< " exprClass=" << exprClassRefp->classp()->prettyNameQ()
<< " exprClassp=" << cvtToHex(exprClassRefp->classp())
<< " result=" << result);
return result;
if (defaultClonep && defaultClonep == exprClassRefp->classp()) return true;
// Slow path: deparameterize the default type and compare the result.
if (!origClassRefp->classp()->hasGParam()) return false;
// const_cast safe: cloneTree doesn't modify the source
AstClassRefDType* const origClonep = static_cast<AstClassRefDType*>(
const_cast<AstClassRefDType*>(origClassRefp)->cloneTree(false));
AstNodeModule* const resolvedModp = classRefDeparam(origClonep, origClassRefp->classp());
const bool match = resolvedModp && VN_CAST(resolvedModp, Class) == exprClassRefp->classp();
VL_DO_DANGLING(origClonep->deleteTree(), origClonep);
return match;
}
static bool paramConstsEqualAtMaxWidth(AstConst* exprp, AstConst* origp) {

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: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('simulator')
test.compile(verilator_flags2=["--binary"])
test.execute()
test.passes()

View File

@ -0,0 +1,94 @@
// DESCRIPTION: Verilator: Verify that nested class type parameters with
// explicit-equivalent defaults resolve to the same specialization.
//
// When uvm_reg_sequence#(type BASE = uvm_sequence#(uvm_reg_item)) is
// extended with the explicit equivalent default, both must produce the
// same specialization so that $cast succeeds between them.
//
// 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: 2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
virtual class uvm_object;
endclass
class uvm_factory;
uvm_object m_type_names[string];
function uvm_object create_object_by_name(string name);
uvm_object wrapper;
if (m_type_names.exists(name)) begin
wrapper = m_type_names[name];
end
else begin
$display("%%Error: object not found '%s'", name);
$stop;
end
return wrapper;
endfunction
function void register(uvm_object obj, string name);
m_type_names[name] = obj;
endfunction
endclass
uvm_factory factory;
class uvm_sequence_item extends uvm_object;
endclass
class uvm_reg_item extends uvm_sequence_item;
endclass
virtual class uvm_sequence #(
type REQ = uvm_sequence_item,
type RSP = REQ
) extends uvm_object;
endclass
class uvm_reg_sequence #(
type BASE = uvm_sequence#(uvm_reg_item)
) extends BASE;
function new;
factory.register(this, "uvm_reg_sequence");
endfunction
endclass
class uvm_reg_hw_reset_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));
function new;
factory.register(this, "uvm_reg_hw_reset_seq");
endfunction
endclass
module t;
initial begin
uvm_reg_hw_reset_seq rsq;
uvm_reg_sequence seq;
uvm_object obj;
int cst;
string seq_name;
factory = new;
rsq = new;
seq_name = "uvm_reg_hw_reset_seq";
obj = factory.create_object_by_name(seq_name);
if (obj == null) $stop;
cst = $cast(seq, obj);
/* verilator lint_off WIDTHTRUNC */
if (!cst || seq == null) begin
$display("%%Error: cast failed");
$stop;
end
/* verilator lint_on WIDTHTRUNC */
if (seq != rsq) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule