Support unbounded '$' in inside range expressions (#6935) (#6938)

This commit is contained in:
Wei-Lun Chiu 2026-01-17 09:03:00 -08:00 committed by GitHub
parent f6532728f6
commit 3b6674386c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 208 additions and 1 deletions

View File

@ -258,6 +258,7 @@ Vassilis Papaefstathiou
Veripool API Bot
Victor Besyakov
Vito Gamberini
Wei-Lun Chiu
William D. Jones
Wilson Snyder
Xi Zhang

View File

@ -1123,6 +1123,16 @@ List Of Warnings
simulate correctly.
.. option:: INSIDETRUE
Warns that an ``inside`` expression contains a range with unbounded
values on both sides (``[$:$]``), which is always true. This is likely
a coding mistake.
Ignoring this warning will only suppress the lint check; it will
simulate correctly.
.. option:: LATCH
.. TODO better example

View File

@ -356,6 +356,34 @@ const char* AstExecGraph::broken() const {
AstNodeExpr* AstInsideRange::newAndFromInside(AstNodeExpr* exprp, AstNodeExpr* lhsp,
AstNodeExpr* rhsp) {
const bool lhsUnbounded = VN_IS(lhsp, Unbounded);
const bool rhsUnbounded = VN_IS(rhsp, Unbounded);
if (lhsUnbounded && rhsUnbounded) {
fileline()->v3warn(INSIDETRUE,
"Unbounded on both sides of inside range [$:$] is always true");
VL_DO_DANGLING(exprp->deleteTree(), exprp);
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
return new AstConst{fileline(), AstConst::BitTrue{}};
}
if (lhsUnbounded) {
// [$:N] - only check expr <= rhs
// Use exprp directly (not cloned) so ExprStmt side effects are preserved
VL_DO_DANGLING(lhsp->deleteTree(), lhsp);
AstNodeExpr* const bp = new AstLte{fileline(), exprp, rhsp};
bp->fileline()->modifyWarnOff(V3ErrorCode::CMPCONST, true);
return bp;
} else if (rhsUnbounded) {
// [N:$] - only check expr >= lhs
VL_DO_DANGLING(rhsp->deleteTree(), rhsp);
AstNodeExpr* const ap = new AstGte{fileline(), exprp, lhsp};
ap->fileline()->modifyWarnOff(V3ErrorCode::UNSIGNED, true);
return ap;
}
// Normal case: [N:M] - check expr >= lhs && expr <= rhs
AstNodeExpr* const ap = new AstGte{fileline(), exprp, lhsp};
AstNodeExpr* lteLhsp;
if (const AstExprStmt* const exprStmt = VN_CAST(exprp, ExprStmt)) {

View File

@ -122,6 +122,7 @@ public:
INFINITELOOP, // Infinite loop
INITIALDLY, // Initial delayed statement
INSECURE, // Insecure options
INSIDETRUE, // Inside range is always true
LATCH, // Latch detected outside of always_latch block
LITENDIAN, // Little endian, renamed to ASCRANGE
MINTYPMAXDLY, // Unsupported: min/typ/max delay expressions
@ -224,7 +225,8 @@ public:
"ENCAPSULATED", "ENDLABEL", "ENUMITEMWIDTH", "ENUMVALUE", "EOFNEWLINE", "FUNCTIMECTL",
"GENCLK", "GENUNNAMED", "HIERBLOCK", "HIERPARAM", "IFDEPTH", "IGNOREDRETURN",
"IMPERFECTSCH", "IMPLICIT", "IMPLICITSTATIC", "IMPORTSTAR", "IMPURE", "INCABSPATH",
"INFINITELOOP", "INITIALDLY", "INSECURE", "LATCH", "LITENDIAN", "MINTYPMAXDLY",
"INFINITELOOP", "INITIALDLY", "INSECURE", "INSIDETRUE", "LATCH", "LITENDIAN",
"MINTYPMAXDLY",
"MISINDENT", "MODDUP", "MODMISSING", "MULTIDRIVEN", "MULTITOP", "NEWERSTD", "NOEFFECT",
"NOLATCH", "NONSTD", "NORETURN", "NULLPORT", "PARAMNODEFAULT", "PINCONNECTEMPTY",
"PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT",

View File

@ -1624,6 +1624,7 @@ class WidthVisitor final : public VNVisitor {
nodep->dtypeSetSigned32(); // Used in int context
if (VN_IS(nodep->backp(), IsUnbounded)) return; // Ok, leave
if (VN_IS(nodep->backp(), BracketArrayDType)) return; // Ok, leave
if (VN_IS(nodep->backp(), InsideRange)) return; // Ok, leave
if (const auto* const varp = VN_CAST(nodep->backp(), Var)) {
if (varp->isParam()) return; // Ok, leave
}

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2026 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')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,80 @@
// DESCRIPTION: Verilator: Test for unbounded '$' in inside range
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026.
// SPDX-License-Identifier: CC0-1.0
module t_inside_unbounded;
initial begin
int value;
// Test [$:100] - should match minimum to 100
value = 50;
if (!(value inside {[$:100]})) $stop;
value = 100;
if (!(value inside {[$:100]})) $stop;
value = 101;
if (value inside {[$:100]}) $stop; // Should NOT match
// Test [0:$] - should match 0 to maximum
value = 50;
if (!(value inside {[0:$]})) $stop;
value = 0;
if (!(value inside {[0:$]})) $stop;
// Test [100:$] - should match 100 to maximum
value = 100;
if (!(value inside {[100:$]})) $stop;
value = 200;
if (!(value inside {[100:$]})) $stop;
value = 50;
if (value inside {[100:$]}) $stop; // Should NOT match
// Test mixed with other ranges
value = 5;
if (!(value inside {[$:10], [90:$]})) $stop;
value = 95;
if (!(value inside {[$:10], [90:$]})) $stop;
value = 50;
if (value inside {[$:10], [90:$]}) $stop; // Should NOT match
// Test with function
if (!(get_value(50) inside {[$:100]})) $stop;
if (!(get_value(50) inside {[0:$]})) $stop;
if (get_value(50) inside {[100:$]}) $stop; // Should NOT match
// Test with increment
value = 49;
if (!(++value inside {[$:100]})) $stop; // value becomes 50
if (value != 50) $stop;
value = -1;
if (!(++value inside {[0:$]})) $stop; // value becomes 0
if (value != 0) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
function int get_value(int v);
return v;
endfunction
// Use volatile-like behavior to prevent compile-time optimization
int runtime_val;
function int get_runtime_value(int v);
`ifdef VERILATOR
runtime_val = $c32(v);
`else
runtime_val = v;
`endif
return runtime_val;
endfunction
endmodule

View File

@ -0,0 +1,6 @@
%Warning-INSIDETRUE: t/t_inside_unbounded_bad.v:12:23: Unbounded on both sides of inside range [$:$] is always true
12 | if (value inside {[$:$]}) $display("PASS");
| ^
... For warning description see https://verilator.org/warn/INSIDETRUE?v=latest
... Use "/* verilator lint_off INSIDETRUE */" and lint_on around source to disable this message.
%Error: Exiting due to

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2026 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('vlt')
test.lint(fails=True, expect_filename=test.golden_filename)
test.passes()

View File

@ -0,0 +1,15 @@
// DESCRIPTION: Verilator: Test for unsupported [$:$] in inside range
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026.
// SPDX-License-Identifier: CC0-1.0
module t_inside_unbounded_bad;
initial begin
int value;
value = 50;
// [$:$] should warn - always true
if (value inside {[$:$]}) $display("PASS");
$finish;
end
endmodule

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2026 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')
test.compile(v_flags2=['-Wno-INSIDETRUE'])
test.execute()
test.passes()

View File

@ -0,0 +1,16 @@
// DESCRIPTION: Verilator: Test for [$:$] with warning suppressed
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2026.
// SPDX-License-Identifier: CC0-1.0
module t_inside_unbounded_nowarn;
initial begin
int value;
value = 50;
// [$:$] is always true - warning suppressed with -Wno-INSIDETRUE
if (!(value inside {[$:$]})) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule