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
Bartosz Skorowski
Benjamin Collier
BRDR LIFE
Brian Li
Cameron Kirk
Cameron Waite

View File

@ -2195,7 +2195,7 @@ class LinkDotFindVisitor final : public VNVisitor {
if (const AstDot* const dotp = VN_CAST(nodep->funcrefp(), Dot))
funcrefp = VN_CAST(dotp->rhsp(), NodeFTaskRef);
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();
AstArg* const argsp = funcrefp->argsp();
if (argsp) {
@ -4298,7 +4298,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
}
UINFO(9, indent() << "randomize-with fromSym " << foundp->nodep());
AstLambdaArgRef* const lambdaRefp
= new AstLambdaArgRef{nodep->fileline(), "item", false};
= new AstLambdaArgRef{nodep->fileline(), "__Vrandwith_obj", false};
AstMemberSel* newp = new AstMemberSel{nodep->fileline(), lambdaRefp,
VFlagChildDType{}, nodep->name()};
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;
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};
nodep->replaceWith(newp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);

View File

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