diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index b84b8ba6c..d67934d68 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2648,6 +2648,46 @@ class LinkDotResolveVisitor final : public VNVisitor { m_ds.init(m_curSymp); iterateNull(nodep); } + static const AstNodeDType* getElemDTypep(const AstNodeDType* dtypep) { + dtypep = dtypep->skipRefp(); + while (true) { + if (const AstBracketArrayDType* const adtypep = VN_CAST(dtypep, BracketArrayDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else if (const AstDynArrayDType* const adtypep = VN_CAST(dtypep, DynArrayDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else if (const AstQueueDType* const adtypep = VN_CAST(dtypep, QueueDType)) { + dtypep = adtypep->childDTypep()->skipRefp(); + } else { + break; + } + } + return dtypep; + } + static const AstNodeDType* getExprDTypep(const AstNodeExpr* selp) { + while (const AstNodePreSel* const sp = VN_CAST(selp, NodePreSel)) selp = sp->fromp(); + if (const AstMemberSel* const sp = VN_CAST(selp, MemberSel)) { + if (const AstNodeDType* dtypep = getExprDTypep(sp->fromp())) { + if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) { + const AstClass* const classp = classRefp->classp(); + const bool found = classp->existsMember( + [&dtypep, name = selp->name()](const AstClass*, const AstVar* nodep) { + dtypep = nodep->childDTypep(); + return nodep->name() == name; + }); + if (found) return getElemDTypep(dtypep); + selp->v3error("Class " << classRefp->prettyNameQ() + << " does not contain field " << selp->prettyNameQ()); + } else { + selp->v3fatalSrc("Member selection on expression of type " + << dtypep->prettyDTypeNameQ() + << ", which is not a class type"); + } + } + } else if (const AstNodeVarRef* const varRefp = VN_CAST(selp, NodeVarRef)) { + return getElemDTypep(varRefp->varp()->childDTypep()); + } + return nullptr; + } #define LINKDOT_VISIT_START() \ VL_RESTORER(m_indent); \ @@ -3746,17 +3786,8 @@ class LinkDotResolveVisitor final : public VNVisitor { if (!fromDtp) { if (const AstNodeVarRef* const varRefp = VN_CAST(nodep->fromp(), NodeVarRef)) { fromDtp = varRefp->varp()->subDTypep(); - } else if (const AstNodeSel* const selp = VN_CAST(nodep->fromp(), NodeSel)) { - if (const AstNodeVarRef* const varRefp - = VN_CAST(selp->fromp(), NodeVarRef)) { - fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep(); - } - } else if (const AstNodePreSel* const selp - = VN_CAST(nodep->fromp(), NodePreSel)) { - if (const AstNodeVarRef* const varRefp - = VN_CAST(selp->fromp(), NodeVarRef)) { - fromDtp = varRefp->varp()->dtypeSkipRefp()->subDTypep(); - } + } else { + fromDtp = getExprDTypep(nodep->fromp()); } if (!fromDtp) { if (VN_IS(nodep->pinsp(), With)) { diff --git a/test_regress/t/t_randomize_complex.py b/test_regress/t/t_randomize_complex.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_complex.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_randomize_complex.v b/test_regress/t/t_randomize_complex.v new file mode 100644 index 000000000..37c04e5b7 --- /dev/null +++ b/test_regress/t/t_randomize_complex.v @@ -0,0 +1,53 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2; + function new (); + sc_inst2 = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst = new; + MyClass cl_inst2 = new; + repeat(10) begin + if (cl_inst.sc_inst.sc_inst1.sc_inst2.randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst.sc_inst.sc_inst1.sc_inst2.field < 1 || cl_inst.sc_inst.sc_inst1.sc_inst2.field > 3) begin + $stop; + end + if (cl_inst2.sc_inst2.randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2.sc_inst2.field < 1 || cl_inst2.sc_inst2.field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_arrays.py b/test_regress/t/t_randomize_complex_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_arrays.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_randomize_complex_arrays.v b/test_regress/t/t_randomize_complex_arrays.v new file mode 100644 index 000000000..f7963ba5a --- /dev/null +++ b/test_regress/t/t_randomize_complex_arrays.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[100]; + MyClass cl_inst2[2]; + cl_inst[1] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_associative_arrays.py b/test_regress/t/t_randomize_complex_associative_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_associative_arrays.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_randomize_complex_associative_arrays.v b/test_regress/t/t_randomize_complex_associative_arrays.v new file mode 100644 index 000000000..69d74ce80 --- /dev/null +++ b/test_regress/t/t_randomize_complex_associative_arrays.v @@ -0,0 +1,55 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[int]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[string]; + MyClass cl_inst2[int]; + cl_inst["val1"] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst["val1"].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_dynamic_arrays.py b/test_regress/t/t_randomize_complex_dynamic_arrays.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_dynamic_arrays.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_randomize_complex_dynamic_arrays.v b/test_regress/t/t_randomize_complex_dynamic_arrays.v new file mode 100644 index 000000000..0b2ee25a3 --- /dev/null +++ b/test_regress/t/t_randomize_complex_dynamic_arrays.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[]; + function new (); + sc_inst2 = new [7]; + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[]; + MyClass cl_inst2[]; + cl_inst = new [3]; + cl_inst2 = new [5]; + cl_inst[1] = new; + cl_inst2[0] = new; + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_member_bad.out b/test_regress/t/t_randomize_complex_member_bad.out new file mode 100644 index 000000000..15f2d826f --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.out @@ -0,0 +1,12 @@ +%Error: t/t_randomize_complex_member_bad.v:36:28: Class 'Deep' does not contain field 'sc_inst2' + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error-UNSUPPORTED: t/t_randomize_complex_member_bad.v:36:49: Unsupported: 'randomize() with' on complex expressions + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~~~~~ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_randomize_complex_member_bad.v:36:67: Can't find definition of variable: 'field' + 36 | if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + | ^~~~~ +%Error: Exiting due to diff --git a/test_regress/t/t_randomize_complex_member_bad.py b/test_regress/t/t_randomize_complex_member_bad.py new file mode 100755 index 000000000..830a90560 --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('linter') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.lint(verilator_flags2=["--lint-only"], fails=True, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_randomize_complex_member_bad.v b/test_regress/t/t_randomize_complex_member_bad.v new file mode 100644 index 000000000..c521a3638 --- /dev/null +++ b/test_regress/t/t_randomize_complex_member_bad.v @@ -0,0 +1,40 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper cl_inst[100]; + cl_inst[1] = new; + if (cl_inst[1].sc_inst.sc_inst2.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + end +endmodule diff --git a/test_regress/t/t_randomize_complex_queue.py b/test_regress/t/t_randomize_complex_queue.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_randomize_complex_queue.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2024 by Wilson Snyder. 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-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_randomize_complex_queue.v b/test_regress/t/t_randomize_complex_queue.v new file mode 100644 index 000000000..1d9e3eef6 --- /dev/null +++ b/test_regress/t/t_randomize_complex_queue.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +class MyClass; + SubClass sc_inst2[$]; + function new (); + SubClass inst = new; + sc_inst2 = { inst }; + endfunction +endclass; +class Deep; + MyClass sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +class WeNeedToGoDeeper; + Deep sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +module t; + initial begin + WeNeedToGoDeeper inst = new; + MyClass inst2 = new; + WeNeedToGoDeeper cl_inst[$] = { inst }; + MyClass cl_inst2[$] = { inst2 }; + repeat(10) begin + if (cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].field < 1 || cl_inst[0].sc_inst.sc_inst1.sc_inst2[0].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[0].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[0].field < 1 || cl_inst2[0].sc_inst2[0].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_complex_typedef.py b/test_regress/t/t_randomize_complex_typedef.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_randomize_complex_typedef.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 by Wilson Snyder. 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-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_randomize_complex_typedef.v b/test_regress/t/t_randomize_complex_typedef.v new file mode 100644 index 000000000..4d8e94589 --- /dev/null +++ b/test_regress/t/t_randomize_complex_typedef.v @@ -0,0 +1,61 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class SubClass; + rand bit [2:0] field; + function new (); + field = 0; + endfunction +endclass +typedef SubClass Sc_t; +class MyClass; + Sc_t sc_inst2[2]; + function new (); + sc_inst2[1] = new; + endfunction +endclass; +typedef MyClass Mc_t; +class Deep; + Mc_t sc_inst1; + function new (); + sc_inst1 = new; + endfunction +endclass; +typedef Deep D_t; +class WeNeedToGoDeeper; + D_t sc_inst; + function new (); + sc_inst = new; + endfunction +endclass; + +typedef WeNeedToGoDeeper WNTGDA_t[100]; +typedef MyClass MCA_t[2]; + +module t; + initial begin + WNTGDA_t cl_inst; + MCA_t cl_inst2; + cl_inst[1] = new; + cl_inst2[0] = new; + repeat(10) begin + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field < 1 || cl_inst[1].sc_inst.sc_inst1.sc_inst2[1].field > 3) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].randomize() with {field inside {1, 2, 3};} == 0) begin + $stop; + end + if (cl_inst2[0].sc_inst2[1].field < 1 || cl_inst2[0].sc_inst2[1].field > 3) begin + $stop; + end + end + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_randomize_method_complex_bad.out b/test_regress/t/t_randomize_method_complex_bad.out index 38c236cd2..01851ca9c 100644 --- a/test_regress/t/t_randomize_method_complex_bad.out +++ b/test_regress/t/t_randomize_method_complex_bad.out @@ -1,14 +1,7 @@ -%Error-UNSUPPORTED: t/t_randomize_method_complex_bad.v:16:11: Unsupported: 'randomize() with' on complex expressions - 16 | x.f.randomize() with { r < 5; }, - | ^~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: t/t_randomize_method_complex_bad.v:16:30: Can't find definition of variable: 'r' - 16 | x.f.randomize() with { r < 5; }, - | ^ - ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_randomize_method_complex_bad.v:17:9: 'randomize() with' on a non-class-instance 'int' 17 | i.randomize() with { v < 5; }); | ^~~~~~~~~ + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. %Error: t/t_randomize_method_complex_bad.v:17:28: Can't find definition of variable: 'v' 17 | i.randomize() with { v < 5; }); | ^