From 5173f7e42f2051c5f0644bf2c85efaa1eecca6fb Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Tue, 17 Feb 2026 04:03:31 +0100 Subject: [PATCH] Fix inside operator crash with impure expression and unsized range literals (#7063) (#7067) --- src/V3Ast.h | 5 +++ src/V3Width.cpp | 26 ++++++++-------- test_regress/t/t_inside_impure_unsized.py | 18 +++++++++++ test_regress/t/t_inside_impure_unsized.v | 37 +++++++++++++++++++++++ 4 files changed, 74 insertions(+), 12 deletions(-) create mode 100755 test_regress/t/t_inside_impure_unsized.py create mode 100644 test_regress/t/t_inside_impure_unsized.v diff --git a/src/V3Ast.h b/src/V3Ast.h index a32396e62..14c1bd3d4 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -781,6 +781,11 @@ public: AstNodeDType* findVoidDType() const; AstNodeDType* findBitDType(int width, int widthMin, VSigning numeric) const; AstNodeDType* findLogicDType(int width, int widthMin, VSigning numeric) const; + AstNodeDType* findBitOrLogicDType(int width, int widthMin, VSigning numeric, + bool isFourstate) const { + return isFourstate ? findLogicDType(width, widthMin, numeric) + : findBitDType(width, widthMin, numeric); + } AstNodeDType* findLogicRangeDType(const VNumRange& range, int widthMin, VSigning numeric) const VL_MT_STABLE; AstNodeDType* findBitRangeDType(const VNumRange& range, int widthMin, diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 22d6c2c18..e138b3869 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2517,10 +2517,8 @@ class WidthVisitor final : public VNVisitor { // So two steps, first do the calculation's width (max of the two widths) { const int calcWidth = std::max(width, underDtp->width()); - AstNodeDType* const calcDtp - = (underDtp->isFourstate() - ? nodep->findLogicDType(calcWidth, calcWidth, underDtp->numeric()) - : nodep->findBitDType(calcWidth, calcWidth, underDtp->numeric())); + AstNodeDType* const calcDtp = nodep->findBitOrLogicDType( + calcWidth, calcWidth, underDtp->numeric(), underDtp->isFourstate()); nodep->dtypep(calcDtp); // We ignore warnings as that is sort of the point of a cast iterateCheck(nodep, "Cast expr", underp, CONTEXT_DET, FINAL, calcDtp, EXTEND_EXP, @@ -2531,10 +2529,8 @@ class WidthVisitor final : public VNVisitor { // UINFOTREE(1, nodep, "", "CastSizeClc"); // Next step, make the proper output width { - AstNodeDType* const outDtp - = (underDtp->isFourstate() - ? nodep->findLogicDType(width, width, underDtp->numeric()) - : nodep->findBitDType(width, width, underDtp->numeric())); + AstNodeDType* const outDtp = nodep->findBitOrLogicDType( + width, width, underDtp->numeric(), underDtp->isFourstate()); nodep->dtypep(outDtp); // We ignore warnings as that is sort of the point of a cast widthCheckSized(nodep, "Cast expr", VN_AS(underp, NodeExpr), outDtp, EXTEND_EXP, @@ -3140,8 +3136,7 @@ class WidthVisitor final : public VNVisitor { } nodep->dtypeSetBit(); const VSigning numeric = nodep->exprp()->dtypep()->numeric(); - expDTypep = isFourstate ? nodep->findLogicDType(width, mwidth, numeric) - : nodep->findBitDType(width, mwidth, numeric); + expDTypep = nodep->findBitOrLogicDType(width, mwidth, numeric, isFourstate); } iterateCheck(nodep, "Inside expression", nodep->exprp(), CONTEXT_DET, FINAL, expDTypep, @@ -3160,8 +3155,15 @@ class WidthVisitor final : public VNVisitor { // executed so, there is no need for purification since they cannot generate sideeffects. if (!m_constraintp && !nodep->exprp()->isPure()) { FileLine* const fl = nodep->exprp()->fileline(); - AstVar* const varp = new AstVar{fl, VVarType::XTEMP, m_insideTempNames.get(nodep), - nodep->exprp()->dtypep()}; + // Ensure sized dtype for temp variable + AstNodeDType* const exprDtp = nodep->exprp()->dtypep(); + const int w = exprDtp->width(); + AstNodeDType* const tempDTypep + = exprDtp->widthSized() ? exprDtp + : nodep->findBitOrLogicDType(w, w, exprDtp->numeric(), + exprDtp->isFourstate()); + AstVar* const varp + = new AstVar{fl, VVarType::XTEMP, m_insideTempNames.get(nodep), tempDTypep}; exprp = new AstVarRef{fl, varp, VAccess::READ}; exprStmtp = new AstExprStmt{fl, new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, diff --git a/test_regress/t/t_inside_impure_unsized.py b/test_regress/t/t_inside_impure_unsized.py new file mode 100755 index 000000000..8a938befd --- /dev/null +++ b/test_regress/t/t_inside_impure_unsized.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_inside_impure_unsized.v b/test_regress/t/t_inside_impure_unsized.v new file mode 100644 index 000000000..7d41a2236 --- /dev/null +++ b/test_regress/t/t_inside_impure_unsized.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 PlanV GmbH +// SPDX-License-Identifier: CC0-1.0 + +module t; + bit [7:0] str_arr[string]; + string str_key; + + bit [7:0] int_arr[int]; + int int_key; + + int counter = 0; + function bit [7:0] get_val(); + counter++; + return 25; + endfunction + + initial begin + str_arr["test"] = 25; + str_key = "test"; + if (!(str_arr[str_key] inside {[10:50]})) $stop; + if (str_arr[str_key] inside {[100:200]}) $stop; + + int_arr[0] = 25; + int_key = 0; + if (!(int_arr[int_key] inside {[10:50]})) $stop; + if (int_arr[int_key] inside {[100:200]}) $stop; + + if (!(get_val() inside {[10:50]})) $stop; + if (get_val() inside {[100:200]}) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule