Fix randomize() with shadowing a user variable named 'item' (#7993) (#7994)

Fixes #7993.
This commit is contained in:
BRDR LIFE 2026-07-28 12:29:45 -04:00 committed by GitHub
parent a6f4dd031f
commit 5fd1c93d43
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 259 additions and 4 deletions

View File

@ -39,6 +39,7 @@ Aylon Chaim Porat
Bartłomiej Chmiel Bartłomiej Chmiel
Bartosz Skorowski Bartosz Skorowski
Benjamin Collier Benjamin Collier
BRDR LIFE
Brian Li Brian Li
Cameron Kirk Cameron Kirk
Cameron Waite Cameron Waite

View File

@ -2195,7 +2195,7 @@ class LinkDotFindVisitor final : public VNVisitor {
if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot)) if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot))
funcrefp = VN_CAST(dotp->rhsp(), NodeFTaskRef); funcrefp = VN_CAST(dotp->rhsp(), NodeFTaskRef);
UASSERT_OBJ(funcrefp, nodep, "'with' only can operate on a function/task"); UASSERT_OBJ(funcrefp, nodep, "'with' only can operate on a function/task");
string name = "item"; string name = funcrefp->name() == "randomize" ? "__Vrandwith_obj" : "item";
FileLine* argFl = nodep->fileline(); FileLine* argFl = nodep->fileline();
AstArg* const argsp = funcrefp->argsp(); AstArg* const argsp = funcrefp->argsp();
if (argsp) { if (argsp) {
@ -4298,7 +4298,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
} }
UINFO(9, indent() << "randomize-with fromSym " << foundp->nodep()); UINFO(9, indent() << "randomize-with fromSym " << foundp->nodep());
AstLambdaArgRef* const lambdaRefp AstLambdaArgRef* const lambdaRefp
= new AstLambdaArgRef{nodep->fileline(), "item", false}; = new AstLambdaArgRef{nodep->fileline(), "__Vrandwith_obj", false};
AstMemberSel* newp = new AstMemberSel{nodep->fileline(), lambdaRefp, AstMemberSel* newp = new AstMemberSel{nodep->fileline(), lambdaRefp,
VFlagChildDType{}, nodep->name()}; VFlagChildDType{}, nodep->name()};
nodep->replaceWith(newp); nodep->replaceWith(newp);
@ -5259,7 +5259,8 @@ class LinkDotResolveVisitor final : public VNVisitor {
} }
if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER; if (m_ds.m_dotPos != DP_NONE) m_ds.m_dotPos = DP_MEMBER;
AstNode* const newp = new AstMethodCall{ AstNode* const newp = new AstMethodCall{
nodep->fileline(), new AstLambdaArgRef{nodep->fileline(), "item", false}, nodep->fileline(),
new AstLambdaArgRef{nodep->fileline(), "__Vrandwith_obj", false},
VFlagChildDType{}, nodep->name(), argsp}; VFlagChildDType{}, nodep->name(), argsp};
nodep->replaceWith(newp); nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep); VL_DO_DANGLING(pushDeletep(nodep), nodep);

View File

@ -1287,7 +1287,7 @@ package Vt_debug_emitv___024unit;
begin : label8 begin : label8
rand_restricted = randomize() with ( rand_restricted = randomize() with (
???? // CONSTRAINTEXPR ???? // CONSTRAINTEXPR
(item.rmember1 < member)) ; (__Vrandwith_obj.rmember1 < member)) ;
disable label8; disable label8;
end end
endfunction endfunction

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,124 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`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
// IEEE 1800-2023 7.12.1 gives array-method 'with' clauses a default iterator
// named 'item'. randomize() with (18.7) has no such iterator, so a user
// variable named 'item' must resolve normally inside the constraint.
class Payload;
rand bit [31:0] addr;
endclass
// The randomized class's own member is spelled 'item'. An undotted name in a
// randomize() with resolves in the object's scope first (18.7), so this must
// reach the member being randomized and not any outer 'item'.
class Tagged;
rand bit [31:0] item;
rand bit [31:0] other;
endclass
// A queue to fold with an array-method 'with', from inside a randomize 'with'.
class Summed;
rand bit [31:0] other;
int q[$] = '{1, 4, 2, 7};
endclass
class Seq;
Payload item; // class member deliberately named 'item'
function new();
item = new();
item.addr = 32'h8000_0084;
endfunction
// Member handle named 'item', dereferenced in the constraint.
function void check_member_handle();
Payload r = new();
int ok;
ok = r.randomize() with {addr == item.addr;};
`checkd(ok, 1)
`checkh(r.addr, 32'h8000_0084)
endfunction
// Same, but not a tautology even when misbound. Before the fix this became
// 'addr == addr + 1' and randomize() returned 0.
function void check_not_tautology();
Payload r = new();
int ok;
ok = r.randomize() with {addr == item.addr + 1;};
`checkd(ok, 1)
`checkh(r.addr, 32'h8000_0085)
endfunction
// A local, rather than a member, also named 'item'.
function void check_local_handle();
Payload r = new();
Payload item = new();
int ok;
item.addr = 32'hdead_beef;
ok = r.randomize() with {addr == item.addr;};
`checkd(ok, 1)
`checkh(r.addr, 32'hdead_beef)
endfunction
// The array-method iterator must keep working, in a class that also has a
// variable named 'item'. This is what the name exists for.
function void check_array_method_iterator();
int q[$] = '{1, 4, 2, 7};
int found[$];
found = q.find with (item > 3);
`checkd(found.size(), 2)
`checkd(found[0], 4)
`checkd(found[1], 7)
endfunction
// A rand member of the randomized class named 'item', referenced undotted.
// The member wins over the caller's own 'item'.
function void check_target_member();
Tagged t = new();
int ok;
ok = t.randomize() with {
item == 32'd42;
other == item + 1;
};
`checkd(ok, 1)
`checkh(t.item, 32'd42)
`checkh(t.other, 32'd43)
endfunction
// An array-method 'with' nested inside a randomize() 'with'. The two now use
// different implicit names, so both must bind: the inner 'item' to the queue
// element, the constraint to the object being randomized.
function void check_nested_iterator();
Summed t = new();
int ok;
ok = t.randomize() with {other == q.sum() with (item);};
`checkd(ok, 1)
`checkh(t.other, 32'd14)
endfunction
endclass
module t ( /*AUTOARG*/);
initial begin
automatic Seq s = new();
s.check_member_handle();
s.check_not_tautology();
s.check_local_handle();
s.check_array_method_iterator();
s.check_target_member();
s.check_nested_iterator();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

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,87 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Wilson Snyder
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`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
// IEEE 1800-2023 7.12.1 gives array-method 'with' clauses a default iterator
// named 'item'. randomize() with (18.7) has no such iterator, so a user
// variable named 'item' must resolve normally inside the constraint.
class Payload;
rand bit [31:0] addr;
endclass
class Other;
bit [31:0] tag;
endclass
class Seq;
Other item; // class member named 'item', of a type with no 'addr'
function new();
item = new();
item.tag = 32'h5a5a_5a5a;
endfunction
// Before the fix 'item' bound to the randomize target, so this failed
// elaboration with "Member 'tag' not found in class 'Payload'".
function void check_other_type();
Payload r = new();
int ok;
ok = r.randomize() with {addr == item.tag;};
`checkd(ok, 1)
`checkh(r.addr, 32'h5a5a_5a5a)
endfunction
// Undotted scalar named 'item'. Before the fix this produced C++ that did
// not compile: "'item' was not declared in this scope".
function void check_bare_scalar();
Payload r = new();
bit [31:0] item = 32'hcafe_f00d;
int ok;
ok = r.randomize() with {addr == item;};
`checkd(ok, 1)
`checkh(r.addr, 32'hcafe_f00d)
endfunction
// A queue member named 'item'. The randomize target has no such member, so
// this must resolve to the caller's queue and index it normally.
function void check_queue_element();
Payload r = new();
bit [31:0] item[$] = '{32'h1111_1111, 32'h2222_2222};
int ok;
ok = r.randomize() with {addr == item[1];};
`checkd(ok, 1)
`checkh(r.addr, 32'h2222_2222)
endfunction
endclass
module t ( /*AUTOARG*/);
// std::randomize() with reaches the 'with' clause through a different call
// site than a class randomize(), and must not shadow 'item' either.
function automatic void check_std_randomize();
bit [31:0] item = 32'h5eed_5eed;
bit [31:0] v;
int ok;
ok = std::randomize(v) with {v == item;};
`checkd(ok, 1)
`checkh(v, 32'h5eed_5eed)
endfunction
initial begin
automatic Seq s = new();
s.check_other_type();
s.check_bare_scalar();
s.check_queue_element();
check_std_randomize();
$write("*-* All Finished *-*\n");
$finish;
end
endmodule