diff --git a/Changes b/Changes index fe0ce08dd..a3756424d 100644 --- a/Changes +++ b/Changes @@ -84,6 +84,7 @@ Verilator 5.043 devel * Fix DFG assertion on out-of-bounds selects. [Geza Lore] * Fix crash when super.new() called without a base class (#6772). [Matthew Ballance] * Fix class-in-class extends with parameters (#6773). +* Fix enum item references in class extends with parameters. Verilator 5.042 2025-11-02 diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 0044b37c2..b268bb154 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1285,11 +1285,13 @@ public: bool cleanOut() const override { return true; } }; class AstEnumItemRef final : public AstNodeExpr { - // @astgen ptr := m_itemp : AstEnumItem // [AfterLink] Pointer to item + // @astgen ptr := m_itemp : Optional[AstEnumItem] // [AfterLink] Pointer to item // @astgen ptr := m_classOrPackagep : Optional[AstNodeModule] // Class/package defined in + string m_name; // Name of enum (for param relink) public: AstEnumItemRef(FileLine* fl, AstEnumItem* itemp, AstNodeModule* classOrPackagep) : ASTGEN_SUPER_EnumItemRef(fl) + , m_name{itemp->name()} , m_itemp{itemp} , m_classOrPackagep{classOrPackagep} { dtypeFrom(m_itemp); @@ -1297,13 +1299,14 @@ public: ASTGEN_MEMBERS_AstEnumItemRef; void dump(std::ostream& str) const override; void dumpJson(std::ostream& str) const override; - string name() const override VL_MT_STABLE { return itemp() ? itemp()->name() : ""; } + string name() const override VL_MT_STABLE { return itemp() ? itemp()->name() : m_name; } int instrCount() const override { return 0; } bool sameNode(const AstNode* samep) const override { const AstEnumItemRef* const sp = VN_DBG_AS(samep, EnumItemRef); return itemp() == sp->itemp(); } AstEnumItem* itemp() const VL_MT_STABLE { return m_itemp; } + void itemp(AstEnumItem* nodep) { m_itemp = nodep; } string emitVerilog() override { V3ERROR_NA_RETURN(""); } string emitC() override { V3ERROR_NA_RETURN(""); } bool cleanOut() const override { return true; } @@ -1691,8 +1694,6 @@ class AstLambdaArgRef final : public AstNodeExpr { // Lambda argument usage // These are not AstVarRefs because we need to be able to delete/clone lambdas during // optimizations and AstVar's are painful to remove. - -private: string m_name; // Name of variable bool m_index; // Index, not value diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 71497a01f..28c45d0ef 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -4370,8 +4370,25 @@ class LinkDotResolveVisitor final : public VNVisitor { } } void visit(AstEnumItemRef* nodep) override { - // EnumItemRef may be under a dot. Should already be resolved. + // Resolve its reference + // EnumItemRefs are created by the first pass, but V3Param may regenerate due to + // a parameterized class/module, so we shouldn't get can't find errors. + // No checkNoDot; created and iterated from a parseRef LINKDOT_VISIT_START(); + if (!nodep->itemp()) { + UINFO(9, indent() << "linkEnumRef se" << cvtToHex(m_curSymp) << " n=" << nodep); + UASSERT_OBJ(m_curSymp, nodep, "nullptr lookup symbol table"); + VSymEnt* const foundp = m_curSymp->findIdFallback(nodep->name()); + if (AstEnumItem* const itemp = foundp ? VN_CAST(foundp->nodep(), EnumItem) : nullptr) { + nodep->itemp(itemp); + nodep->classOrPackagep(foundp->classOrPackagep()); + UINFO(9, indent() << " resolved " << nodep); + } + if (VL_UNCOVERABLE(!nodep->itemp())) { + nodep->v3error("Can't find definition of enum item, again: " // LCOV_EXCL_LINE + << nodep->prettyNameQ()); + } + } iterateChildren(nodep); } void visit(AstMethodCall* nodep) override { diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 2c82c2af0..d06687883 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -2032,8 +2032,12 @@ public: } // Set all links pointing to a user3 (deleting) node as null netlistp->foreach([](AstNode* const nodep) { - nodep->foreachLink([](AstNode** const linkpp, const char*) { - if (*linkpp && (*linkpp)->user3()) *linkpp = nullptr; + nodep->foreachLink([&](AstNode** const linkpp, const char*) { + if (*linkpp && (*linkpp)->user3()) { + UINFO(9, "clear link " << nodep); + *linkpp = nullptr; + UINFO(9, "cleared link " << nodep); + } }); }); } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index c6c3416c7..91120095c 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2817,6 +2817,7 @@ class WidthVisitor final : public VNVisitor { } } void visit(AstEnumItemRef* nodep) override { + UASSERT_OBJ(nodep->itemp(), nodep, "Unlinked"); if (!nodep->itemp()->didWidth()) { // We need to do the whole enum en masse AstNode* enump = nodep->itemp(); diff --git a/test_regress/t/t_enum_param_class2.py b/test_regress/t/t_enum_param_class2.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_enum_param_class2.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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() diff --git a/test_regress/t/t_enum_param_class2.v b/test_regress/t/t_enum_param_class2.v new file mode 100644 index 000000000..2dc5f76fc --- /dev/null +++ b/test_regress/t/t_enum_param_class2.v @@ -0,0 +1,34 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +// verilog_format: on + +package uvm_pkg; + class uvm_reg_sequence #( + type BASE = int + ); + typedef enum { + LOCAL = 2, + UPSTREAM = 3 + } seq_parent_e; + endclass +endpackage + +module t; + import uvm_pkg::*; + class test_seq extends uvm_reg_sequence; + endclass + initial begin + test_seq c; + c = new; + `checkd(c.LOCAL, 2); + `checkd(c.UPSTREAM, 3); + $finish; + end +endmodule