Fix std::randomize() in static function with static class members (#7167) (#7169)

This commit is contained in:
Yilou Wang 2026-03-02 11:59:00 +01:00 committed by GitHub
parent 108d209bd7
commit 8a413b3ec7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 100 additions and 3 deletions

View File

@ -2180,6 +2180,7 @@ class CaptureVisitor final : public VNVisitor {
std::map<const AstVar*, AstVar*> m_varCloneMap; // Map original var nodes to their clones
std::set<AstNode*> m_ignore; // Nodes to ignore for capturing
AstVar* m_thisp = nullptr; // Variable for outer context's object, if necessary
bool m_staticContext = false; // True when capturing from a static function context
// METHODS
@ -2252,6 +2253,11 @@ class CaptureVisitor final : public VNVisitor {
if (varIsParam) return CaptureMode::CAP_VALUE;
// Static var in function (will not be inlined, because it's in class)
if (callerIsClass && varIsFuncLocal) return CaptureMode::CAP_VALUE;
// Static class members in static context don't need 'this' capture;
// V3Class will move both the function and the member to __Vclpkg
if (m_staticContext && callerIsClass && varIsFieldOfCaller
&& varRefp->varp()->lifetime().isStatic())
return CaptureMode::CAP_NO;
if (callerIsClass && varIsFieldOfCaller) return CaptureMode::CAP_THIS;
UASSERT_OBJ(!callerIsClass, varRefp, "Invalid reference?");
return CaptureMode::CAP_VALUE;
@ -2372,10 +2378,12 @@ class CaptureVisitor final : public VNVisitor {
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const targetp)
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const targetp,
bool staticContext = false)
: m_argsp{nullptr}
, m_callerp{callerp}
, m_targetp{targetp} {
, m_targetp{targetp}
, m_staticContext{staticContext} {
iterateAndNextNull(nodep);
}
@ -3771,9 +3779,16 @@ class RandomizeVisitor final : public VNVisitor {
if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
// Handle std::randomize; create wrapper function that calls basicStdRandomization on
// each varref argument, then transform nodep to call that wrapper
const bool inStaticContext = m_ftaskp && m_ftaskp->isStatic();
AstVar* const stdrand = createStdRandomGenerator(m_modp);
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc(
m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep));
// When called from a static function, mark helper and stdrand as static
// so V3Class moves them to __Vclpkg alongside the calling function
if (inStaticContext) {
stdrand->lifetime(VLifetime::STATIC_EXPLICIT);
randomizeFuncp->isStatic(true);
}
randomizeFuncp->addStmtsp(
new AstAssign{nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
@ -3829,7 +3844,8 @@ class RandomizeVisitor final : public VNVisitor {
}
if (withp) {
FileLine* const fl = nodep->fileline();
withCapturep = std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr);
withCapturep = std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr,
inStaticContext);
withCapturep->addFunctionArguments(randomizeFuncp);
// Clear old constraints and variables for std::randomize with clause
if (stdrand) {

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,60 @@
// 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
// Test std::randomize() called from a static function referencing static
// class members in the 'with' clause.
// 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
module t;
typedef enum bit [3:0] {
INSTR_ADD = 0,
INSTR_SUB = 1,
INSTR_MUL = 2,
INSTR_AND = 4
} instr_name_t;
class instr_base;
static instr_name_t allowed_instrs[$];
static function void init();
allowed_instrs.push_back(INSTR_ADD);
allowed_instrs.push_back(INSTR_SUB);
allowed_instrs.push_back(INSTR_MUL);
allowed_instrs.push_back(INSTR_AND);
endfunction
static function instr_name_t get_rand_instr();
instr_name_t name;
int ok;
ok = std::randomize(name) with {
name inside {allowed_instrs};
};
`checkd(ok, 1);
return name;
endfunction
endclass
initial begin
instr_name_t result;
instr_base::init();
repeat (20) begin
result = instr_base::get_rand_instr();
`checkd(result == INSTR_ADD || result == INSTR_SUB
|| result == INSTR_MUL || result == INSTR_AND, 1);
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule