From 075d624b291e0e5d979e8aef13678f697a10b698 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 9 Dec 2025 19:22:59 -0500 Subject: [PATCH] Add SUPERNFIRST error on super.new on non-first statement (#6784). --- Changes | 1 + docs/gen/ex_SUPERNFIRST_faulty.rst | 8 +++++ docs/gen/ex_SUPERNFIRST_msg.rst | 4 +++ docs/guide/warnings.rst | 18 ++++++++++++ src/V3Error.h | 16 +++++----- src/V3Width.cpp | 27 +++++++++++++++++ .../t/t_class_new_supernfirst_bad.out | 9 ++++++ test_regress/t/t_class_new_supernfirst_bad.py | 24 +++++++++++++++ test_regress/t/t_class_new_supernfirst_bad.v | 29 +++++++++++++++++++ 9 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 docs/gen/ex_SUPERNFIRST_faulty.rst create mode 100644 docs/gen/ex_SUPERNFIRST_msg.rst create mode 100644 test_regress/t/t_class_new_supernfirst_bad.out create mode 100755 test_regress/t/t_class_new_supernfirst_bad.py create mode 100644 test_regress/t/t_class_new_supernfirst_bad.v diff --git a/Changes b/Changes index 9ed46cec9..85236c0a8 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.043 devel * Add `-param`/`-port` options to `public_flat*` control directives (#6685). [Geza Lore, Fractile Ltd.] * Add `--top` specifying `config` name (#6710). [Dan Ruelas-Petrisko] * Add `sc_biguint` pragma (#6712). [Jakub Wasilewski, Antmicro Ltd.] +* Add SUPERNFIRST error on super.new on non-first statement (#6784). [Artur Bieniek] * Support `std::randomize(){...}` (#4706) (#6573). [Yilou Wang] * Support `config` instance clauses (#5891 partial) (#6745). [Dan Ruelas-Petrisko] * Support unpacked struct in localparam (#6053 partial) (#6708). [Jonathan Drolet] diff --git a/docs/gen/ex_SUPERNFIRST_faulty.rst b/docs/gen/ex_SUPERNFIRST_faulty.rst new file mode 100644 index 000000000..dcbed941e --- /dev/null +++ b/docs/gen/ex_SUPERNFIRST_faulty.rst @@ -0,0 +1,8 @@ +.. comment: generated by t_class_new_supernfirst_bad +.. code-block:: sv + :linenos: + :emphasize-lines: 3 + + function new(); + int x = $random(); + super.new(build_coverage(x), x); // <--- BAD, must be first statement diff --git a/docs/gen/ex_SUPERNFIRST_msg.rst b/docs/gen/ex_SUPERNFIRST_msg.rst new file mode 100644 index 000000000..87f016a48 --- /dev/null +++ b/docs/gen/ex_SUPERNFIRST_msg.rst @@ -0,0 +1,4 @@ +.. comment: generated by t_class_new_supernfirst_bad +.. code-block:: + + %Error-SUPERNFIRST: example.v:1:11 'super.new' must be first statement in a 'function new' (IEEE 1800-2023 8.15) diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 34ff72c9e..011789b24 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -2003,6 +2003,24 @@ List Of Warnings * Run Verilator with :vlopt:`--timing`. +.. option:: SUPERNFIRST + + An error that a `super.new` is not the first statement in a `function + new`. + + IEEE requires this error. Ignoring this warning may cause other errors + or initialization ordering surprises, as described in IEEE 1800-2023 + 8.15. + + Faulty example: + + .. include:: ../../docs/gen/ex_SUPERNFIRST_faulty.rst + + Results in: + + .. include:: ../../docs/gen/ex_SUPERNFIRST_msg.rst + + .. option:: SYMRSVDWORD Warning that a symbol matches a C++ reserved word, and using this as a diff --git a/src/V3Error.h b/src/V3Error.h index 6292f7c11..5b083a3c6 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -158,6 +158,7 @@ public: SPLITVAR, // Cannot split the variable STATICVAR, // Static variable declared in a loop with a declaration assignment STMTDLY, // Delayed statement + SUPERNFIRST, // Super.new must be first statement SYMRSVDWORD, // Symbol is Reserved Word SYNCASYNCNET, // Mixed sync + async reset TICKCOUNT, // Too large tick count @@ -224,11 +225,12 @@ public: "PINMISSING", "PINNOCONNECT", "PINNOTFOUND", "PKGNODECL", "PREPROCZERO", "PROCASSINIT", "PROCASSWIRE", "PROFOUTOFDATE", "PROTECTED", "PROTOTYPEMIS", "RANDC", "REALCVT", "REDEFMACRO", "RISEFALLDLY", "SELRANGE", "SHORTREAL", "SIDEEFFECT", "SPECIFYIGN", - "SPLITVAR", "STATICVAR", "STMTDLY", "SYMRSVDWORD", "SYNCASYNCNET", "TICKCOUNT", - "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", "UNPACKED", - "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", "USERERROR", - "USERFATAL", "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", "WIDTHCONCAT", - "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", " MAX"}; + "SPLITVAR", "STATICVAR", "STMTDLY", "SUPERNFIRST", "SYMRSVDWORD", "SYNCASYNCNET", + "TICKCOUNT", "TIMESCALEMOD", "UNDRIVEN", "UNOPT", "UNOPTFLAT", "UNOPTTHREADS", + "UNPACKED", "UNSIGNED", "UNUSEDGENVAR", "UNUSEDLOOP", "UNUSEDPARAM", "UNUSEDSIGNAL", + "USERERROR", "USERFATAL", "USERINFO", "USERWARN", "VARHIDDEN", "WAITCONST", "WIDTH", + "WIDTHCONCAT", "WIDTHEXPAND", "WIDTHTRUNC", "WIDTHXZEXPAND", "ZERODLY", "ZEROREPL", + " MAX"}; return names[m_e]; } // Warnings that default to off @@ -258,8 +260,8 @@ public: || m_e == ENDLABEL || m_e == ENUMITEMWIDTH || m_e == ENUMVALUE || m_e == HIERPARAM || m_e == FUNCTIMECTL || m_e == IMPURE || m_e == MODMISSING || m_e == PARAMNODEFAULT || m_e == PINNOTFOUND || m_e == PKGNODECL - || m_e == PROCASSWIRE || m_e == PROTOTYPEMIS || m_e == ZEROREPL // Says IEEE - ); + || m_e == PROCASSWIRE || m_e == PROTOTYPEMIS || m_e == SUPERNFIRST + || m_e == ZEROREPL); } // Warnings to mention manual bool mentionManual() const VL_MT_SAFE { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index f6ee174ae..d5be92247 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -6337,6 +6337,33 @@ class WidthVisitor final : public VNVisitor { nodep->v3error("class 'new()' cannot be virual (IEEE 1800-2023 18.3)"); if (nodep->isStatic()) nodep->v3error("class 'new()' cannot be static (IEEE 1800-2023 18.3)"); + AstNode* firstp = nullptr; + for (AstNode* itemp = nodep->stmtsp(); itemp; itemp = itemp->nextp()) { + if (AstStmtExpr* const sep = VN_CAST(itemp, StmtExpr)) { + if (AstNew* const newp = VN_CAST(sep->exprp(), New)) { + if (firstp) { + UINFOTREE(1, firstp, "", "-earlier"); + newp->v3warn(SUPERNFIRST, + "'super.new' must be first statement in a 'function " + "new' (IEEE 1800-2023 8.15)\n" + << newp->warnContextPrimary() << '\n' + << firstp->warnOther() + << "... Location of earlier statement\n" + << firstp->warnContextSecondary()); + break; + } + } + continue; + } + if (AstVar* const varp = VN_CAST(itemp, Var)) { + if (!varp->valuep() || VN_CAST(varp->valuep(), Const) || varp->isIO()) + continue; + } + if (AstAssign* const aitemp = VN_CAST(itemp, Assign)) { + if (VN_IS(aitemp->rhsp(), Const)) continue; + } + firstp = itemp; + } } // Function hasn't been widthed, so make it so. // Would use user1 etc, but V3Width called from too many places to spend a user diff --git a/test_regress/t/t_class_new_supernfirst_bad.out b/test_regress/t/t_class_new_supernfirst_bad.out new file mode 100644 index 000000000..e1df28028 --- /dev/null +++ b/test_regress/t/t_class_new_supernfirst_bad.out @@ -0,0 +1,9 @@ +%Error-SUPERNFIRST: t/t_class_new_supernfirst_bad.v:20:11: 'super.new' must be first statement in a 'function new' (IEEE 1800-2023 8.15) + : ... note: In instance 't' + 20 | super.new(build_coverage(x), x); + | ^~~ + t/t_class_new_supernfirst_bad.v:19:13: ... Location of earlier statement + 19 | int x = $random(); + | ^~~~~~~ + ... For error description see https://verilator.org/warn/SUPERNFIRST?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_class_new_supernfirst_bad.py b/test_regress/t/t_class_new_supernfirst_bad.py new file mode 100755 index 000000000..5ca16f5c9 --- /dev/null +++ b/test_regress/t/t_class_new_supernfirst_bad.py @@ -0,0 +1,24 @@ +#!/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') + +test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename) + +test.extract(in_filename=test.top_filename, + out_filename=test.root + "/docs/gen/ex_SUPERNFIRST_faulty.rst", + lines="18-20") + +test.extract(in_filename=test.golden_filename, + out_filename=test.root + "/docs/gen/ex_SUPERNFIRST_msg.rst", + regexp=r'SUPERNFIRST:') + +test.passes() diff --git a/test_regress/t/t_class_new_supernfirst_bad.v b/test_regress/t/t_class_new_supernfirst_bad.v new file mode 100644 index 000000000..1728b4776 --- /dev/null +++ b/test_regress/t/t_class_new_supernfirst_bad.v @@ -0,0 +1,29 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class base_reg_block; + function new(string name, int x); + if (name == x) $finish; + endfunction + + function string build_coverage(int x); + return $sformatf("%0d", x); + endfunction +endclass + +class spi_reg_block extends base_reg_block; + function new(); + int x = $random(); + super.new(build_coverage(x), x); // <--- BAD, must be first statement + endfunction +endclass + +module t; + initial begin + spi_reg_block test = new; + $finish; + end +endmodule