Fix inherited rand array with .size + foreach constraint (#7650)

Fixes #7558.
This commit is contained in:
Yilou Wang 2026-05-24 21:02:01 +08:00 committed by GitHub
parent f93b4bbd05
commit 6c96ce4b40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 130 additions and 4 deletions

View File

@ -4818,7 +4818,12 @@ class RandomizeVisitor final : public VNVisitor {
pinp->addPinsp(namep);
pinp->addPinsp(new AstConst{fl, AstConst::Unsized64{},
static_cast<uint64_t>(sizeVarp->width())});
pinp->addPinsp(new AstVarRef{fl, sizeVarp, VAccess::READ});
// sizeVarp may live in a base class when the constrained
// array is inherited; route VarRef through its declaring
// class so V3Scope can resolve it.
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
sizeVarRefp->classOrPackagep(VN_AS(sizeVarp->user2p(), NodeModule));
pinp->addPinsp(sizeVarRefp);
randomizep->addStmtsp(pinp->makeStmt());
}
@ -5265,12 +5270,18 @@ class RandomizeVisitor final : public VNVisitor {
bool wasCreated = false;
AstVar* const sizeVarp
= createOrGetSizeVar(classp, arrVarp, fl, methodp->findIntDType(), wasCreated);
// arrVarp and sizeVarp may live in a base class when the
// array is inherited; route VarRefs through their declaring
// class so V3Scope can resolve them.
AstNodeModule* const arrClassp = VN_AS(arrVarp->user2p(), NodeModule);
AstNodeModule* const sizeClassp = VN_AS(sizeVarp->user2p(), NodeModule);
if (wasCreated) {
// Generate resize for dynamic arrays/queues (not assoc arrays)
if (!VN_IS(arrVarp->dtypep()->skipRefp(), AssocArrayDType)) {
AstCMethodHard* const resizep = new AstCMethodHard{
fl, new AstVarRef{fl, classp, arrVarp, VAccess::READWRITE},
VCMethod::DYN_RESIZE, new AstVarRef{fl, sizeVarp, VAccess::READ}};
fl, new AstVarRef{fl, arrClassp, arrVarp, VAccess::READWRITE},
VCMethod::DYN_RESIZE,
new AstVarRef{fl, sizeClassp, sizeVarp, VAccess::READ}};
resizep->dtypep(methodp->findVoidDType());
inlineResizeStmtsp
= AstNode::addNext(inlineResizeStmtsp, new AstStmtExpr{fl, resizep});
@ -5279,7 +5290,8 @@ class RandomizeVisitor final : public VNVisitor {
// Append size >= 0 constraint so ConstraintExprVisitor processes it
capturedTreep->addNext(createSizeGteZeroConstraint(fl, sizeVarp));
}
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
AstVarRef* const sizeVarRefp
= new AstVarRef{fl, sizeClassp, sizeVarp, VAccess::READ};
sizeVarRefp->user1(true);
methodp->replaceWith(sizeVarRefp);
VL_DO_DANGLING(methodp->deleteTree(), methodp);

View File

@ -0,0 +1,21 @@
#!/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')
if not test.have_solver:
test.skip("No constraint solver installed")
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,93 @@
// 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
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
// Scenario A: minimized reproducer from issue #7558 reporter (2026-05-15).
// Dynamic array, parameterized virtual class wrapping the rand class,
// 3-level extends, randomize() reached indirectly via `req` field.
package uvm_pkg;
virtual class uvm_sequence #(type REQ = int);
REQ req;
endclass
endpackage
package s_t_pkg;
import uvm_pkg::*;
parameter S_D_WIDTH = 64;
class s_txn_base;
rand logic [S_D_WIDTH-1:0] p_d[];
rand int d_s_dw;
constraint d_s_dw_c {
p_d.size() == d_s_dw / 16;
d_s_dw inside {[16 : 80]};
}
endclass
class s_t_txn extends s_txn_base;
constraint d_incremental {
foreach (p_d[i]) if (i == 0) p_d[i] == '0;
}
endclass
class t_base_sequence extends uvm_sequence#(s_t_txn);
endclass
class t_base_port_seq extends t_base_sequence;
endclass
class p_c_seq extends t_base_port_seq;
virtual task body();
void'(req.randomize());
endtask
endclass
endpackage
// Scenario B: same bug class but on a queue (rand int q[$]) -- exercises
// the queue dtype dispatch in V3Randomize, distinct from dynamic array.
package q_pkg;
class q_base;
rand int q[$];
constraint c_size {q.size() == 3;}
endclass
class q_derived extends q_base;
constraint c_inc {foreach (q[i]) q[i] == i * 10;}
endclass
endpackage
module t;
import s_t_pkg::*;
import q_pkg::*;
initial begin
// Scenario A: solomatnikov reproducer
static p_c_seq seq = new();
seq.req = new();
repeat (5) begin
seq.body();
`checkd(seq.req.p_d.size(), seq.req.d_s_dw / 16)
if (seq.req.p_d.size() > 0) `checkd(seq.req.p_d[0], 0)
end
// Scenario B: queue extension
begin
automatic q_derived qd = new();
repeat (5) begin
`checkd(qd.randomize(), 1)
`checkd(qd.q.size(), 3)
foreach (qd.q[i]) `checkd(qd.q[i], i * 10)
end
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule