From 02c1dbc5dcd6398941b25021a3801aa927f1a07f Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Sat, 28 Feb 2026 09:52:02 -0500 Subject: [PATCH] Fix lambda coroutines (#6106) (#7135) --- src/V3EmitCFunc.h | 18 +++++- src/V3Width.cpp | 4 +- test_regress/t/t_coroutine_lambda.py | 21 +++++++ test_regress/t/t_coroutine_lambda.v | 83 ++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_coroutine_lambda.py create mode 100644 test_regress/t/t_coroutine_lambda.v diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 1684cc1ca..1ee0d3aa5 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -1199,7 +1199,7 @@ public: if (VN_IS(nodep->exprp()->dtypep()->skipRefp(), VoidDType)) { putns(nodep, ""); } else { - putns(nodep, "(void)"); // Prevent unused expression warning in C + putns(nodep, "std::ignore = "); } iterateConst(nodep->exprp()); puts(";\n"); @@ -1293,6 +1293,22 @@ public: } void visit(AstExprStmt* nodep) override { VL_RESTORER(m_createdScopeHash); + const bool containsAwait = nodep->exists([](AstCAwait*) -> bool { return true; }); + if (containsAwait) { + UASSERT_OBJ(m_cfuncp && m_cfuncp->isCoroutine(), nodep, + "AstExprStmt with CAwait must be in coroutine"); + putnbs(nodep, "(co_await ([&]() -> VlCoroutine {\n"); + iterateAndNextConstNull(nodep->stmtsp()); + puts("co_return;\n"); + if (!nodep->hasResult()) { + puts("}()))"); + return; + } + puts("}()), "); + iterateAndNextConstNull(nodep->resultp()); + puts(")"); + return; + } // GCC allows compound statements in expressions, but this is not standard. // So we use an immediate-evaluation lambda and comma operator putnbs(nodep, "([&]() {\n"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index de98e9de6..a2e050928 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4504,7 +4504,7 @@ class WidthVisitor final : public VNVisitor { = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), VCMethod::RNG_SET_RANDSTATE, exprp->unlinkFrBack()}; newp->usePtr(true); - newp->dtypeSetString(); + newp->dtypeSetVoid(); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return; @@ -6994,7 +6994,7 @@ class WidthVisitor final : public VNVisitor { = new AstCExpr{nodep->fileline(), "__Vm_rng.set_randstate(", 1}; newp->add(exprp->unlinkFrBack()); newp->add(")"); - newp->dtypeSetString(); + newp->dtypeSetVoid(); nodep->replaceWith(newp); VL_DO_DANGLING(pushDeletep(nodep), nodep); return; diff --git a/test_regress/t/t_coroutine_lambda.py b/test_regress/t/t_coroutine_lambda.py new file mode 100755 index 000000000..a4be4c39b --- /dev/null +++ b/test_regress/t/t_coroutine_lambda.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Coroutine call inside AstExprStmt lambda +# +# 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') + +test.compile(verilator_flags2=[ + "--binary", + "--timing", + "-Wno-FUNCTIMECTL", + "-Wno-WIDTHTRUNC", +]) + +test.passes() diff --git a/test_regress/t/t_coroutine_lambda.v b/test_regress/t/t_coroutine_lambda.v new file mode 100644 index 000000000..c0a7eb6c0 --- /dev/null +++ b/test_regress/t/t_coroutine_lambda.v @@ -0,0 +1,83 @@ +// DESCRIPTION: Verilator: Coroutine call inside AstExprStmt lambda test module +// +// 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 +interface cpu_if(input logic clk); +endinterface + +package p; + +virtual class WriterIf; + virtual function void write(input int t); + endfunction +endclass + +class BlockingWriter; + virtual cpu_if vif; + task write(int t); + @(posedge vif.clk); + endtask +endclass + +class WriterAdapter extends WriterIf; + BlockingWriter m_impl; + function new(BlockingWriter impl); + m_impl = impl; + endfunction + function void write(input int t); + m_impl.write(t); // function -> task path + endfunction +endclass + +class QueueLike; + WriterIf sink; + mailbox #(int) m; + function bit try_get(output int t); + if (!m.try_get(t)) begin + end + sink.write(t); // can become coroutine call + endfunction +endclass + +class DriverLike; + QueueLike reqq; + function void item_done(); + int t; + if (reqq.try_get(t) == 0) begin + end + endfunction +endclass + +endpackage + +module t; + import p::*; + + logic clk = 0; + cpu_if vif(clk); + + always #1 clk = ~clk; + + initial begin + BlockingWriter writer; + WriterAdapter adapter; + QueueLike reqq; + DriverLike drv; + + writer = new(); + writer.vif = vif; + adapter = new(writer); + + reqq = new(); + reqq.sink = adapter; + + drv = new(); + drv.reqq = reqq; + drv.item_done(); + + #2 $finish; + end +endmodule