Fix side-effect loss when slicing array expressions (#7427) (#7429)

Fixes #7427.
This commit is contained in:
Rowan Goemans 2026-04-15 14:10:11 +02:00 committed by GitHub
parent 7f571971ca
commit c143c2fdd2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 25 deletions

View File

@ -230,6 +230,7 @@ Risto Pejašinović
Robert Balas
Robin Heinemann
Rodrigo Batista de Moraes
Rowan Goemans
Rupert Swarbrick
Ryan Ziegler
Ryszard Rozak

View File

@ -105,8 +105,8 @@ class SliceVisitor final : public VNVisitor {
newp = nullptr;
int itemIdx = 0;
int i = 0;
const AstInitArray::KeyItemMap& itemMap = initp->map();
if (const int prevItemIdx = initp->user2()) {
const AstInitArray::KeyItemMap& itemMap = initp->map();
const auto it = itemMap.find(considerOrder(arrayp, prevItemIdx));
if (it != itemMap.end()) {
const AstInitItem* itemp = it->second;
@ -119,8 +119,10 @@ class SliceVisitor final : public VNVisitor {
}
const AstNodeDType* const expectedItemDTypep = arrayp->subDTypep()->skipRefp();
while (i <= elemIdx) {
const auto itemIt = itemMap.find(considerOrder(arrayp, itemIdx));
AstNodeExpr* const itemp
= initp->getIndexDefaultedValuep(considerOrder(arrayp, itemIdx));
= itemIt != itemMap.end() ? itemIt->second->valuep() : initp->defaultp();
const bool directItem = itemIt != itemMap.end();
if (!itemp && !m_assignError) {
nodep->v3error("Array initialization has too few elements, need element "
<< elemIdx);
@ -132,7 +134,7 @@ class SliceVisitor final : public VNVisitor {
= AstNode::computeCastable(expectedItemDTypep, itemRawDTypep, itemp);
if (castable == VCastable::SAMEISH || castable == VCastable::COMPATIBLE) {
if (i == elemIdx) {
newp = itemp->cloneTreePure(false);
newp = itemp->cloneTree(false, !directItem && needPure);
break;
} else { // Check the next item
++i;
@ -184,7 +186,6 @@ class SliceVisitor final : public VNVisitor {
m_assignError = true;
}
if (newp) {
const AstInitArray::KeyItemMap& itemMap = initp->map();
const auto it = itemMap.find(considerOrder(arrayp, itemIdx));
if (it != itemMap.end()) { // Remember current position for the next invocation.
initp->user2(itemIdx);
@ -283,16 +284,6 @@ class SliceVisitor final : public VNVisitor {
AstNodeAssign* const newp
= nodep->cloneType(cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0),
cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0));
if (elemIdx == 0) {
nodep->foreach([this](AstExprStmt* const exprp) {
// Result expression is always evaluated to the same value, so the statements
// can be removed once they were included in the expression created for the 1st
// element.
AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack();
exprp->replaceWith(resultp);
VL_DO_DANGLING(pushDeletep(exprp), exprp);
});
}
UINFOTREE(9, newp, "", "new");
newlistp = AstNode::addNext(newlistp, newp);
}
@ -356,17 +347,6 @@ class SliceVisitor final : public VNVisitor {
T_NodeBiop* const clonep = new T_NodeBiop{
nodep->fileline(), cloneAndSel(nodep->lhsp(), elements, elemIdx, elemIdx != 0),
cloneAndSel(nodep->rhsp(), elements, elemIdx, elemIdx != 0)};
if (elemIdx == 0) {
nodep->foreach([this](AstExprStmt* const exprp) {
// Result expression is always evaluated to the same value, so the
// statements can be removed once they were included in the expression
// created for the 1st element.
AstNodeExpr* const resultp = exprp->resultp()->unlinkFrBack();
exprp->replaceWith(resultp);
VL_DO_DANGLING(pushDeletep(exprp), exprp);
});
}
if (!logp) {
logp = clonep;
} else {

View File

@ -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()

View File

@ -0,0 +1,49 @@
// DESCRIPTION: Verilator: Assignment pattern preserves array expression side effects
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-FileCopyrightText: 2026 Rowan Goemans
// 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
module t;
// verilator lint_off ASCRANGE
typedef logic [0:2][7:0] triple_lv_t;
// verilator lint_on ASCRANGE
typedef triple_lv_t pair_t [0:1];
function automatic triple_lv_t mk3(
input logic [7:0] a,
input logic [7:0] b,
input logic [7:0] c
);
mk3 = '{0: a, 1: b, 2: c};
endfunction
pair_t pair;
initial begin
// verilator lint_off SIDEEFFECT
pair = '{
0: mk3(8'd1, 8'd2, 8'd3),
1: mk3(8'd4, 8'd5, 8'd6)
};
// verilator lint_on SIDEEFFECT
`checkd(pair[0][0], 8'd1);
`checkd(pair[0][1], 8'd2);
`checkd(pair[0][2], 8'd3);
`checkd(pair[1][0], 8'd4);
`checkd(pair[1][1], 8'd5);
`checkd(pair[1][2], 8'd6);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule