From 45a5e725090b9631e97abde508e55eb14a520b59 Mon Sep 17 00:00:00 2001 From: Julian Carrier Date: Thu, 5 Mar 2026 16:05:54 -0500 Subject: [PATCH] Fix recursive default assignment for subarrays (#4589) (#7202) --- docs/CONTRIBUTORS | 1 + src/V3Const.cpp | 25 +++++++++--- src/V3Width.cpp | 30 ++++++++++++++- .../t/t_array_pattern_default_recursive.py | 18 +++++++++ .../t/t_array_pattern_default_recursive.v | 38 +++++++++++++++++++ test_regress/t/t_assert_elab_p.out | 2 +- 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100755 test_regress/t/t_array_pattern_default_recursive.py create mode 100644 test_regress/t/t_array_pattern_default_recursive.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index a865eefe5..d04f9760f 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -134,6 +134,7 @@ Jose Loyola Josep Sans Joseph Nwabueze Josh Redford +Julian Carrier Julian Daube Julie Schwartz Julien Margetts diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 507c9ae41..43e2b2f94 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2667,12 +2667,27 @@ class ConstVisitor final : public VNVisitor { iterateChildren(nodep); if (const AstInitArray* const initp = VN_CAST(nodep->lhsp(), InitArray)) { if (!(m_doExpensive || m_params)) return false; - // At present only support 1D unpacked arrays - const auto initOfConst = [](const AstNode* const nodep) -> bool { // - return VN_IS(nodep, Const) || VN_IS(nodep, InitItem); + const auto isConstInit = [](const AstNode* const exprp, + const auto& isConstInitRecurse) -> bool { + if (VN_IS(exprp, Const)) return true; + if (const AstInitItem* const itemp = VN_CAST(exprp, InitItem)) { + return isConstInitRecurse(itemp->valuep(), isConstInitRecurse); + } + if (const AstInitArray* const arrayp = VN_CAST(exprp, InitArray)) { + const auto itemIsConstInit = [&isConstInitRecurse](const AstNode* const itemp) + -> bool { + return isConstInitRecurse(itemp, isConstInitRecurse); + }; + if (arrayp->initsp() && !arrayp->initsp()->forall(itemIsConstInit)) return false; + if (arrayp->defaultp() + && !isConstInitRecurse(arrayp->defaultp(), isConstInitRecurse)) { + return false; + } + return true; + } + return false; }; - if (initp->initsp() && !initp->initsp()->forall(initOfConst)) return false; - if (initp->defaultp() && !initp->defaultp()->forall(initOfConst)) return false; + if (!isConstInit(initp, isConstInit)) return false; } else if (!VN_IS(nodep->lhsp(), Const)) { return false; } diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 06415443a..cb778eed9 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -5282,6 +5282,34 @@ class WidthVisitor final : public VNVisitor { return newp; } + AstPatMember* defaultPatp_patternArray(AstPatMember* defaultp, AstNodeDType* elemDTypep) { + AstNodeExpr* const valuep = defaultp->lhssp()->cloneTree(false); + AstNodeDType* const elemDTypeSkipRefp = elemDTypep->skipRefp(); + + if (!VN_IS(elemDTypeSkipRefp, UnpackArrayDType)) { + VL_DO_DANGLING(pushDeletep(valuep), valuep); + return defaultp->cloneTree(false); + } + if (VN_IS(valuep, Pattern)) { + VL_DO_DANGLING(pushDeletep(valuep), valuep); + return defaultp->cloneTree(false); + } + if (!valuep->dtypep()) userIterate(valuep, WidthVP{SELF, BOTH}.p()); + if (valuep->dtypep() + && AstNode::computeCastable(valuep->dtypep()->skipRefp(), elemDTypeSkipRefp, nullptr) + .isAssignable()) { + VL_DO_DANGLING(pushDeletep(valuep), valuep); + return defaultp->cloneTree(false); + } + + AstPatMember* const nestedDefaultp + = new AstPatMember{defaultp->fileline(), valuep, nullptr, nullptr}; + nestedDefaultp->isDefault(true); + AstPattern* const recursivePatternp + = new AstPattern{defaultp->fileline(), nestedDefaultp}; + return new AstPatMember{defaultp->fileline(), recursivePatternp, nullptr, nullptr}; + } + void patternArray(AstPattern* nodep, AstNodeArrayDType* arrayDtp, AstPatMember* defaultp) { const VNumRange range = arrayDtp->declRange(); PatVecMap patmap = patVectorMap(nodep, range); @@ -5296,7 +5324,7 @@ class WidthVisitor final : public VNVisitor { const auto it = patmap.find(ent); if (it == patmap.end()) { if (defaultp) { - newpatp = defaultp->cloneTree(false); + newpatp = defaultPatp_patternArray(defaultp, arrayDtp->subDTypep()); patp = newpatp; } else if (!(VN_IS(arrayDtp, UnpackArrayDType) && !allConstant && isConcat)) { // If arrayDtp is an unpacked array and item is not constant, diff --git a/test_regress/t/t_array_pattern_default_recursive.py b/test_regress/t/t_array_pattern_default_recursive.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_array_pattern_default_recursive.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() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_array_pattern_default_recursive.v b/test_regress/t/t_array_pattern_default_recursive.v new file mode 100644 index 000000000..c179307ce --- /dev/null +++ b/test_regress/t/t_array_pattern_default_recursive.v @@ -0,0 +1,38 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +module t; + + int arr_default_scalar[4][4]; + int row[4]; + int arr_default_array[2][4]; + int arr_mixed_default[2][3]; + + initial begin + arr_default_scalar = '{default: 0}; + foreach (arr_default_scalar[i, j]) begin + if (arr_default_scalar[i][j] != 0) $stop; + end + + row = '{1, 2, 3, 4}; + arr_default_array = '{default: row}; + foreach (arr_default_array[i, j]) begin + if (arr_default_array[i][j] != row[j]) $stop; + end + + arr_mixed_default = '{0: '{0: 1, default: 3}, default: '{default: 2}}; + if (arr_mixed_default[0][0] != 1) $stop; + if (arr_mixed_default[0][1] != 3) $stop; + if (arr_mixed_default[0][2] != 3) $stop; + if (arr_mixed_default[1][0] != 2) $stop; + if (arr_mixed_default[1][1] != 2) $stop; + if (arr_mixed_default[1][2] != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_assert_elab_p.out b/test_regress/t/t_assert_elab_p.out index f2d5d12ff..2ef285fb3 100644 --- a/test_regress/t/t_assert_elab_p.out +++ b/test_regress/t/t_assert_elab_p.out @@ -1,4 +1,4 @@ --Info: t/t_assert_elab_p.v:14:5: %m test.sv:25: 4=4 2=2 STAGE_IDS='{'h1, 'h1, 'h1, 'h1} +-Info: t/t_assert_elab_p.v:14:5: %m test.sv:25: 4=4 2=2 STAGE_IDS='{'{'h1, 'h1}, '{'h1, 'h1}, '{'h1, 'h1}, '{'h1, 'h1}} : ... note: In instance 't.pipe' 14 | $info("%m %s:%0d: 4=%0d 2=%0d STAGE_IDS=%p", "test.sv", 25, 4, 2, STAGE_IDS); | ^~~~~