diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 13e31e2e3..3504d07bf 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -1235,30 +1235,31 @@ 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( + const_cast(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) { diff --git a/test_regress/t/t_uvm_typeof_type.py b/test_regress/t/t_uvm_typeof_type.py new file mode 100755 index 000000000..6fe7d000c --- /dev/null +++ b/test_regress/t/t_uvm_typeof_type.py @@ -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() diff --git a/test_regress/t/t_uvm_typeof_type.v b/test_regress/t/t_uvm_typeof_type.v new file mode 100644 index 000000000..076275d2d --- /dev/null +++ b/test_regress/t/t_uvm_typeof_type.v @@ -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