From 16fdd0e93c5286e2bfd982c8fee9c4d78fb43890 Mon Sep 17 00:00:00 2001 From: Artur Bieniek Date: Mon, 13 Oct 2025 13:29:23 +0200 Subject: [PATCH] Fiix fault on operations on uninitialized strings (#6520) Signed-off-by: Artur Bieniek --- src/V3AstNodeExpr.h | 5 ++++ src/V3EmitCFunc.h | 5 ++++ src/V3Task.cpp | 33 +++++++++++++++++++++++-- test_regress/t/t_func_call_super_arg.py | 18 ++++++++++++++ test_regress/t/t_func_call_super_arg.v | 28 +++++++++++++++++++++ 5 files changed, 87 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_func_call_super_arg.py create mode 100644 test_regress/t/t_func_call_super_arg.v diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 374483b85..9c068f6c6 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1266,6 +1266,9 @@ class AstExprStmt final : public AstNodeExpr { // resultp is evaluated AFTER the statement(s). // @astgen op1 := stmtsp : List[AstNode] // @astgen op2 := resultp : AstNodeExpr +private: + bool m_hasResult = true; + public: AstExprStmt(FileLine* fl, AstNode* stmtsp, AstNodeExpr* resultp) : ASTGEN_SUPER_ExprStmt(fl) { @@ -1283,6 +1286,8 @@ public: return resultp()->isPure(); } bool sameNode(const AstNode*) const override { return true; } + bool hasResult() { return m_hasResult; } + void hasResult(bool flag) { m_hasResult = flag; } }; class AstFError final : public AstNodeExpr { // @astgen op1 := filep : AstNode diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index f14b483e6..a6f1a6361 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1227,6 +1227,11 @@ public: // GCC allows compound statements in expressions, but this is not standard. // So we use an immediate-evaluation lambda and comma operator putnbs(nodep, "([&]() {\n"); + if (!nodep->hasResult()) { + iterateAndNextConstNull(nodep->stmtsp()); + puts("}())"); + return; + } iterateAndNextConstNull(nodep->stmtsp()); puts("}(), "); iterateAndNextConstNull(nodep->resultp()); diff --git a/src/V3Task.cpp b/src/V3Task.cpp index fefe843ee..416fd81f5 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -374,6 +374,7 @@ class TaskVisitor final : public VNVisitor { AstScope* m_scopep = nullptr; // Current scope AstNode* m_insStmtp = nullptr; // Where to insert statement bool m_inSensesp = false; // Are we under a senitem? + bool m_inNew = false; // Are we under a constructor? int m_modNCalls = 0; // Incrementing func # for making symbols // STATE - across all visitors @@ -1476,6 +1477,11 @@ class TaskVisitor final : public VNVisitor { iterateChildren(nodep); UASSERT_OBJ(!m_insStmtp, nodep, "Didn't finish out last statement"); } + void visit(AstCNew* nodep) override { + VL_RESTORER(m_inNew); + m_inNew = true; + iterateChildren(nodep); + } void visit(AstNodeFTaskRef* nodep) override { if (m_inSensesp && !nodep->isPure()) { nodep->v3warn(E_UNSUPPORTED, @@ -1525,10 +1531,33 @@ class TaskVisitor final : public VNVisitor { UASSERT_OBJ(nodep->taskp()->isFunction(), nodep, "funcref-like expression to non-function"); AstVarRef* const outrefp = new AstVarRef{nodep->fileline(), outvscp, VAccess::READ}; - beginp = new AstExprStmt{nodep->fileline(), beginp, outrefp}; + AstExprStmt* lambdap = new AstExprStmt{nodep->fileline(), beginp, outrefp}; + + if (m_inNew) { + AstVar* varp = outvscp->varp(); + varp->funcLocal(true); + + // Create a new var that will be inside the lambda + AstVar* newvarp = varp->cloneTree(false); + + // Replace all references so they point to the new var + lambdap->stmtsp()->foreachAndNext([varp, newvarp](AstVarRef* refp) { + if (refp->varp() == varp) refp->varp(newvarp); + }); + + // Add variable initialization + lambdap->stmtsp()->addHereThisAsNext(newvarp); + lambdap->hasResult(false); + + // Add return statement + AstCExpr* const exprp = new AstCExpr{nodep->fileline(), varp->name(), 0}; + exprp->dtypeSetString(); + lambdap->addStmtsp(new AstCReturn{nodep->fileline(), exprp}); + } + // AstExprStmt is currently treated as impure, so clear the cached purity of its // parents - nodep->replaceWith(beginp); + nodep->replaceWith(lambdap); VL_DO_DANGLING(nodep->deleteTree(), nodep); VIsCached::clearCacheTree(); } else { // VN_IS(nodep->backp(), StmtExpr) diff --git a/test_regress/t/t_func_call_super_arg.py b/test_regress/t/t_func_call_super_arg.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_func_call_super_arg.py @@ -0,0 +1,18 @@ +#!/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') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_func_call_super_arg.v b/test_regress/t/t_func_call_super_arg.v new file mode 100644 index 000000000..e3429ec61 --- /dev/null +++ b/test_regress/t/t_func_call_super_arg.v @@ -0,0 +1,28 @@ +// 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 base; + function new(string name); + $display(name); + if(name == "42") $finish; + endfunction + + function string retstr(); + return $sformatf("%0d", $c("42")); + endfunction +endclass + +class derived extends base; + function new(); + super.new(retstr()); + endfunction +endclass + +module t; + initial begin + derived test = new; + end +endmodule