From 1a91ab02cf723214b2220d0c167ef78dc9d6aec4 Mon Sep 17 00:00:00 2001 From: Igor Zaworski Date: Thu, 9 Oct 2025 16:08:06 +0200 Subject: [PATCH] Fix passing typedef value as parameter (#6543) --- src/V3LinkDot.cpp | 8 ++++---- src/V3Param.cpp | 23 ++++++++++++++++++++++ src/V3Width.cpp | 15 ++++++++------ src/V3Width.h | 3 +++ test_regress/t/t_param_typedef2.py | 18 +++++++++++++++++ test_regress/t/t_param_typedef2.v | 28 +++++++++++++++++++++++++++ test_regress/t/t_typedef_circ_bad.out | 3 +++ 7 files changed, 88 insertions(+), 10 deletions(-) create mode 100755 test_regress/t/t_param_typedef2.py create mode 100644 test_regress/t/t_param_typedef2.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index ef3a12cb6..2a227a2ec 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2889,14 +2889,14 @@ class LinkDotResolveVisitor final : public VNVisitor { dtypep = dtypep->skipRefp(); while (true) { if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) { - dtypep = adtypep->childDTypep()->skipRefp(); + dtypep = adtypep->subDTypep()->skipRefp(); } else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) { - dtypep = adtypep->childDTypep()->skipRefp(); + dtypep = adtypep->subDTypep()->skipRefp(); } else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) { - dtypep = adtypep->childDTypep()->skipRefp(); + dtypep = adtypep->subDTypep()->skipRefp(); } else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) { - dtypep = adtypep->childDTypep()->skipRefp(); + dtypep = adtypep->subDTypep()->skipRefp(); } else { break; } diff --git a/src/V3Param.cpp b/src/V3Param.cpp index f73f6969e..3127601e6 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -62,6 +62,7 @@ #include #include +#include #include #include @@ -1265,6 +1266,7 @@ public: class ParamVisitor final : public VNVisitor { // NODE STATE // AstNodeModule::user1 -> bool: already fixed level (temporary) + // AstNodeDType::user2 -> bool: already visited from typedef // STATE - across all visitors ParamState& m_state; // Common state @@ -1279,6 +1281,8 @@ class ParamVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) AstNodeModule* m_modp; // Module iterating string m_generateHierName; // Generate portion of hierarchy name + std::map + m_isCircularTypeCache; // Caches return values of `V3Width::isCircularType` calls // METHODS @@ -1413,6 +1417,25 @@ class ParamVisitor final : public VNVisitor { processWorkQ(); } } + void visit(AstRefDType* nodep) override { + const auto iter = m_isCircularTypeCache.find(nodep); + bool isCircular; + if (iter != m_isCircularTypeCache.end()) { + isCircular = iter->second; + } else { + isCircular = V3Width::isCircularType(nodep); + m_isCircularTypeCache.emplace(nodep, isCircular); + } + if (isCircular) { + nodep->v3error("Typedef's type is circular: " << nodep->prettyName()); + } else if (nodep->typedefp() && nodep->subDTypep() + && (VN_IS(nodep->subDTypep()->skipRefOrNullp(), IfaceRefDType) + || VN_IS(nodep->subDTypep()->skipRefOrNullp(), ClassRefDType)) + && !nodep->skipRefp()->user2SetOnce()) { + iterate(nodep->skipRefp()); + } + iterateChildren(nodep); + } void visit(AstCell* nodep) override { checkParamNotHier(nodep->paramsp()); visitCellOrClassRef(nodep, VN_IS(nodep->modp(), Iface)); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 8ed305187..99336502d 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2108,12 +2108,6 @@ class WidthVisitor final : public VNVisitor { UINFO(4, "dtWidthed " << nodep); } void visit(AstRefDType* nodep) override { - if (nodep->doingWidth()) { // Early exit if have circular parameter definition - nodep->v3error("Typedef's type is circular: " << nodep->prettyName()); - nodep->dtypeSetBit(); - nodep->doingWidth(false); - return; - } if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed nodep->doingWidth(true); if (nodep->typeofp()) { // type(typeofp_expression) @@ -8958,3 +8952,12 @@ AstNode* V3Width::widthGenerateParamsEdit( // No WidthRemoveVisitor, as don't want to drop $signed etc inside gen blocks return nodep; } + +bool V3Width::isCircularType(const AstRefDType* nodep) { + std::set visited; + while (nodep) { + if (!visited.insert(nodep).second) return true; + nodep = VN_CAST(nodep->subDTypep(), RefDType); + } + return false; +} diff --git a/src/V3Width.h b/src/V3Width.h index 88eb86c2a..b4a977941 100644 --- a/src/V3Width.h +++ b/src/V3Width.h @@ -23,6 +23,7 @@ class AstNetlist; class AstNode; class AstNodeDType; +class AstRefDType; //============================================================================ @@ -36,6 +37,8 @@ public: // Replace AstSelBit, etc with AstSel/AstArraySel // Returns replacement node if nodep was deleted, or null if none. static AstNode* widthSelNoIterEdit(AstNode* nodep) VL_MT_DISABLED; + + static bool isCircularType(const AstRefDType* nodep); }; #endif // Guard diff --git a/test_regress/t/t_param_typedef2.py b/test_regress/t/t_param_typedef2.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_param_typedef2.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_param_typedef2.v b/test_regress/t/t_param_typedef2.v new file mode 100644 index 000000000..dcf6db506 --- /dev/null +++ b/test_regress/t/t_param_typedef2.v @@ -0,0 +1,28 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Foo #(type T); +endclass + +class Bar #(type T); +endclass + +typedef Bar#(string) s_t; + +class Some #(type T); + typedef Bar#(string) s_t; +endclass + +module t; + initial begin + Some#(int)::s_t x[string]; + Bar#(string) y[string]; + if ($typename(Foo#(s_t)) != $typename(Foo#(Bar#(string)))) $stop; + if ($typename(x) != $typename(y)) $stop; + $write("*-* All finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_typedef_circ_bad.out b/test_regress/t/t_typedef_circ_bad.out index 0848e912e..ab2253ce5 100644 --- a/test_regress/t/t_typedef_circ_bad.out +++ b/test_regress/t/t_typedef_circ_bad.out @@ -2,4 +2,7 @@ 8 | typedef a_t b_t; | ^~~ ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: t/t_typedef_circ_bad.v:9:9: Typedef's type is circular: b_t + 9 | typedef b_t a_t; + | ^~~ %Error: Exiting due to