From ae642facf5fb921533bfca1fafa0752f734b48b5 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Thu, 23 Apr 2026 16:47:39 +0200 Subject: [PATCH] Fix function-in-constraint internal error on bare port VarRef return (#7473) (#7480) Fixes #7473. --- src/V3Randomize.cpp | 15 +++- .../t/t_constraint_func_call_root_varref.py | 21 +++++ .../t/t_constraint_func_call_root_varref.v | 88 +++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_constraint_func_call_root_varref.py create mode 100644 test_regress/t/t_constraint_func_call_root_varref.v diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index c14490faa..44a3e8d79 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -2560,12 +2560,23 @@ class ConstraintExprVisitor final : public VNVisitor { const V3TaskConnects tconnects = V3Task::taskConnects(nodep, funcp->stmtsp(), nullptr, false); - // Clone return expression, substitute params with args - AstNodeExpr* const inlinedp = retExprp->cloneTreePure(false); + // Clone return expression, substitute params with args. + // Track the root via a local pointer: when the root itself is a VarRef to a port + // (e.g. body `F = d;`), the cloned root has no back pointer, so foreach's + // replaceWith would trip the "no back" assertion -- substitute the root pointer + // directly instead. + AstNodeExpr* inlinedp = retExprp->cloneTreePure(false); for (const auto& tconnect : tconnects) { const AstVar* const portp = tconnect.first; AstArg* const argp = tconnect.second; if (!argp || !argp->exprp()) continue; + if (AstVarRef* const rootRefp = VN_CAST(inlinedp, VarRef)) { + if (rootRefp->varp() == portp) { + inlinedp = argp->exprp()->cloneTreePure(false); + VL_DO_DANGLING(rootRefp->deleteTree(), rootRefp); + continue; + } + } inlinedp->foreach([&](AstVarRef* refp) { if (refp->varp() == portp) { refp->replaceWith(argp->exprp()->cloneTreePure(false)); diff --git a/test_regress/t/t_constraint_func_call_root_varref.py b/test_regress/t/t_constraint_func_call_root_varref.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_func_call_root_varref.py @@ -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() diff --git a/test_regress/t/t_constraint_func_call_root_varref.v b/test_regress/t/t_constraint_func_call_root_varref.v new file mode 100644 index 000000000..3f81b1bb7 --- /dev/null +++ b/test_regress/t/t_constraint_func_call_root_varref.v @@ -0,0 +1,88 @@ +// 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 + +class FassignBody; + rand int b1; + rand int b2; + function int F(input int d); + F = d; + endfunction + constraint c1 {b1 inside {[4'd1 : 4'd9]};} + constraint c2 {b2 == F(b1);} +endclass + +class Freturn; + rand int b1; + rand int b2; + function int F(input int d); + return d; + endfunction + constraint c1 {b1 inside {[4'd1 : 4'd9]};} + constraint c2 {b2 == F(b1);} +endclass + +class Fmultiarg; + rand int b1; + rand int b2; + rand int b3; + function int F(input int x, input int y); + F = y; + endfunction + constraint c1 {b1 inside {[4'd1 : 4'd9]};} + constraint c2 {b2 inside {[8'd20 : 8'd40]};} + constraint c3 {b3 == F(b1, b2);} +endclass + +class Fnonrand; + rand int b2; + int k; + function int F(input int d); + F = d; + endfunction + constraint c {b2 == F(k);} +endclass + +module t; + FassignBody o1; + Freturn o2; + Fmultiarg o3; + Fnonrand o4; + int rand_ok; + + initial begin + o1 = new; + o2 = new; + o3 = new; + o4 = new; + + repeat (20) begin + rand_ok = o1.randomize(); + `checkd(rand_ok, 1) + `checkd(o1.b2, o1.b1) + + rand_ok = o2.randomize(); + `checkd(rand_ok, 1) + `checkd(o2.b2, o2.b1) + + rand_ok = o3.randomize(); + `checkd(rand_ok, 1) + `checkd(o3.b3, o3.b2) + + o4.k = $urandom_range(32'd100, 32'd1); + rand_ok = o4.randomize(); + `checkd(rand_ok, 1) + `checkd(o4.b2, o4.k) + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule