Fix inline constraint on array-indexed randomize target (#7431) (#7434)

Fixes #7431.
This commit is contained in:
Yilou Wang 2026-04-16 17:02:22 +02:00 committed by GitHub
parent 3587ac48a4
commit 72952fd3fb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 33 additions and 5 deletions

View File

@ -107,7 +107,7 @@ class LinkWithVisitor final : public VNVisitor {
}
void visit(AstMemberSel* nodep) override {
if (m_inRandomizeWith && nodep->fromp()->isSame(m_currentRandomizeSelectp)) {
if (m_inRandomizeWith && nodep->fromp()->sameTree(m_currentRandomizeSelectp)) {
// Replace member selects to the element
// on which the randomize() is called with LambdaArgRef
// This allows V3Randomize to work properly when

View File

@ -753,6 +753,7 @@ class ConstraintExprVisitor final : public VNVisitor {
// (used to format "%s.%s" for struct arrays)
std::set<std::string>& m_writtenVars; // Track which variable paths have write_var generated
// (shared across all constraints)
std::set<std::string> m_inlineWrittenVars; // Per-instance tracking for inline constraints
std::set<AstVar*>* m_sizeConstrainedArraysp = nullptr; // Arrays with size+element constraints
// Build full path for a MemberSel chain (e.g., "obj.l2.l3.l4")
@ -1133,14 +1134,17 @@ class ConstraintExprVisitor final : public VNVisitor {
// else: Global constraints keep nodep alive for write_var processing
relinker.relink(exprp);
// For global constraints: check if this specific path has been written
// For normal constraints: only call write_var if varp->user3() is not set
const bool alreadyWritten
= isGlobalConstrained ? m_writtenVars.count(smtName) > 0 : varp->user3();
// For global constraints: check shared path-level set
// For inline constraints: check per-instance set (each __Vrandwith has own randomizer)
// For class-level constraints: check varp->user3()
const bool alreadyWritten = isGlobalConstrained ? m_writtenVars.count(smtName) > 0
: m_inlineInitTaskp ? m_inlineWrittenVars.count(smtName) > 0
: varp->user3();
const bool shouldWriteVar = !alreadyWritten;
if (shouldWriteVar) {
// Track this variable path as written
if (isGlobalConstrained) m_writtenVars.insert(smtName);
if (m_inlineInitTaskp) m_inlineWrittenVars.insert(smtName);
// For global constraints, delete nodep after processing
if (isGlobalConstrained && !nodep->backp()) VL_DO_DANGLING(pushDeletep(nodep), nodep);

View File

@ -39,6 +39,7 @@ endclass
module t;
OuterDyn od;
OuterDyn od_arr[5];
OuterQueue oq;
initial begin
@ -104,6 +105,29 @@ module t;
end
end
// === Test 4: Array of objects with inline constraint for sub-object members ===
// Verifies member resolution AND runtime enforcement when randomize()
// target is array-indexed (regression test for verilator/verilator#7431)
foreach (od_arr[i]) begin
od_arr[i] = new(3);
assert(od_arr[i].randomize() with {
od_arr[i].items[0].val > 8'd10;
od_arr[i].items[0].val < 8'd200;
od_arr[i].items[0].tag > 0;
} != 0);
if (!(od_arr[i].items[0].val > 10 && od_arr[i].items[0].val < 200)) begin
$display("FAIL: od_arr[%0d].items[0].val=%0d out of range",
i, od_arr[i].items[0].val);
$stop;
end
if (od_arr[i].items[0].tag == 0) begin
$display("FAIL: od_arr[%0d].items[0].tag=%0d should be > 0",
i, od_arr[i].items[0].tag);
$stop;
end
end
$write("*-* All Finished *-*\n");
$finish;
end