[clang] [CIR] Upstream support co_return of values from co_await (PR #173174)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jan 13 18:20:19 PST 2026
https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/173174
>From 37736e80e0069479c5245cc9a2e99fc0b869f6b3 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Sat, 20 Dec 2025 18:56:53 -0500
Subject: [PATCH 1/2] [CIR] Upstream support co_return of values from co_await
---
clang/include/clang/CIR/MissingFeatures.h | 1 -
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 11 ++-
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 16 +++-
clang/test/CIR/CodeGen/coro-task.cpp | 85 +++++++++++++++++++
.../CIR/CodeGen/trivial-ctor-const-init.cpp | 10 ++-
5 files changed, 115 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index acfc937a11993..78199adec1a05 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -152,7 +152,6 @@ struct MissingFeatures {
static bool coroEndBuiltinCall() { return false; }
static bool emitBodyAndFallthrough() { return false; }
static bool coroOutsideFrameMD() { return false; }
- static bool coroCoReturn() { return false; }
static bool coroCoYield() { return false; }
static bool coroutineExceptions() { return false; };
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 9251106a641b1..b636022251f0c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -422,10 +422,17 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
} else {
awaitRes.rv =
cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
- if (!awaitRes.rv.isIgnored())
+ if (!awaitRes.rv.isIgnored()) {
// Create the alloca in the block before the scope wrapping
// cir.await.
- assert(!cir::MissingFeatures::coroCoReturn());
+ tmpResumeRValAddr = cgf.emitAlloca(
+ "__coawait_resume_rval", awaitRes.rv.getValue().getType(), loc,
+ CharUnits::One(),
+ builder.getBestAllocaInsertPoint(scopeParentBlock));
+ // Store the rvalue so we can reload it before the promise call.
+ builder.CIRBaseBuilderTy::createStore(loc, awaitRes.rv.getValue(),
+ tmpResumeRValAddr);
+ }
}
if (tryStmt)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 367c56f07f734..77cff8d3c3f59 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -722,7 +722,21 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
CIRGenFunction::RunCleanupsScope cleanups(cgf);
- Visit(e->getSubExpr());
+ auto &builder = cgf.getBuilder();
+ auto scopeLoc = cgf.getLoc(e->getSourceRange());
+ mlir::OpBuilder::InsertPoint scopeBegin;
+ cir::ScopeOp::create(builder, scopeLoc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ scopeBegin = b.saveInsertionPoint();
+ });
+
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.restoreInsertionPoint(scopeBegin);
+ CIRGenFunction::LexicalScope lexScope{cgf, scopeLoc,
+ builder.getInsertionBlock()};
+ Visit(e->getSubExpr());
+ }
}
void AggExprEmitter::VisitCallExpr(const CallExpr *e) {
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index 6cd494317f2d8..2c4686e27a460 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -322,3 +322,88 @@ folly::coro::Task<void> silly_coro() {
// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
// CIR-NOT: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
// CIR: cir.await(final, ready : {
+
+folly::coro::Task<int> go(int const& val);
+folly::coro::Task<int> go1() {
+ auto task = go(1);
+ co_return co_await task;
+}
+
+// CIR: cir.func coroutine {{.*}} @_Z3go1v() {{.*}} ![[IntTask]]
+// CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], !cir.ptr<![[IntTask]]>, ["task", init]
+
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }
+
+// The call to go(1) has its own scope due to full-expression rules.
+// CIR: cir.scope {
+// CIR: %[[OneAddr:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp1", init] {alignment = 4 : i64}
+// CIR: %[[One:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.store{{.*}} %[[One]], %[[OneAddr]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[IntTaskTmp:.*]] = cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>) -> ![[IntTask]]
+// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: }
+
+// CIR: %[[CoReturnValAddr:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["__coawait_resume_rval"] {alignment = 1 : i64}
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: %[[ResumeVal:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%3)
+// CIR: cir.store{{.*}} %[[ResumeVal]], %[[CoReturnValAddr]] : !s32i, !cir.ptr<!s32i>
+// CIR: },)
+// CIR: %[[V:.*]] = cir.load{{.*}} %[[CoReturnValAddr]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}}, %[[V]])
+
+
+folly::coro::Task<int> go1_lambda() {
+ auto task = []() -> folly::coro::Task<int> {
+ co_return 1;
+ }();
+ co_return co_await task;
+}
+
+// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]]
+// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() {{.*}} ![[IntTask]]
+
+folly::coro::Task<int> go4() {
+ auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
+ auto task = fn(3);
+ co_return co_await std::move(task);
+}
+
+// CIR: cir.func coroutine {{.*}} @_Z3go4v() {{.*}} ![[IntTask]]
+
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }
+
+// CIR: %[[RES:.*]] = cir.scope {
+// CIR: %[[LAMBDA:.*]] = cir.alloca !rec_anon2E2, !cir.ptr<!rec_anon2E2>, ["ref.tmp1"] {alignment = 1 : i64}
+
+// Get the lambda invoker ptr via `lambda operator folly::coro::Task<int> (*)(int const&)()`
+// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%[[LAMBDA]]) nothrow : (!cir.ptr<!rec_anon2E2>) -> !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: %[[PLUS:.*]] = cir.unary(plus, %[[INVOKER]]) : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: cir.yield %[[PLUS]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: }
+// CIR: cir.store{{.*}} %[[RES]], %[[PTR_TASK:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
+// CIR: cir.scope {
+// CIR: %[[ARG:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp2", init] {alignment = 4 : i64}
+// CIR: %[[LAMBDA2:.*]] = cir.load{{.*}} %[[PTR_TASK]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
+// CIR: cir.store{{.*}} %[[THREE]], %[[ARG]] : !s32i, !cir.ptr<!s32i>
+
+// Call invoker, which calls operator() indirectly.
+// CIR: %[[RES:.*]] = cir.call %[[LAMBDA2]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!s32i>) -> ![[IntTask]]
+// CIR: cir.store{{.*}} %[[RES]], %4 : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: }
+
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }
diff --git a/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp b/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp
index 7429549100362..76c0dd208cb64 100644
--- a/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp
+++ b/clang/test/CIR/CodeGen/trivial-ctor-const-init.cpp
@@ -27,11 +27,13 @@ StructWithCtorArg withArg = 0.0;
// OGCG: @withArg = global %struct.StructWithCtorArg zeroinitializer
// CIR: cir.func {{.*}} @__cxx_global_var_init()
-// CIR: %[[TMP0:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["ref.tmp0"]
// CIR: %[[WITH_ARG:.*]] = cir.get_global @withArg : !cir.ptr<!rec_StructWithCtorArg>
-// CIR: %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00> : !cir.double
-// CIR: cir.store{{.*}} %[[ZERO]], %[[TMP0]] : !cir.double, !cir.ptr<!cir.double>
-// CIR: cir.call @_ZN17StructWithCtorArgC1ERKd(%[[WITH_ARG]], %[[TMP0]]) : (!cir.ptr<!rec_StructWithCtorArg>, !cir.ptr<!cir.double>) -> ()
+// CIR: cir.scope {
+// CIR: %[[TMP0:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["ref.tmp0"]
+// CIR: %[[ZERO:.*]] = cir.const #cir.fp<0.000000e+00> : !cir.double
+// CIR: cir.store{{.*}} %[[ZERO]], %[[TMP0]] : !cir.double, !cir.ptr<!cir.double>
+// CIR: cir.call @_ZN17StructWithCtorArgC1ERKd(%[[WITH_ARG]], %[[TMP0]]) : (!cir.ptr<!rec_StructWithCtorArg>, !cir.ptr<!cir.double>) -> ()
+// CIR: }
// LLVM: define {{.*}} void @__cxx_global_var_init()
// LLVM: %[[TMP0:.*]] = alloca double
>From a989176437992529344351a4c251ccbef69bfc69 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Tue, 13 Jan 2026 21:19:30 -0500
Subject: [PATCH 2/2] Address review comments
---
clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 77cff8d3c3f59..1f51d4829024a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -13,6 +13,7 @@
#include "CIRGenBuilder.h"
#include "CIRGenFunction.h"
#include "CIRGenValue.h"
+#include "mlir/IR/Builders.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/AST/Expr.h"
@@ -722,9 +723,16 @@ void AggExprEmitter::VisitLambdaExpr(LambdaExpr *e) {
void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *e) {
CIRGenFunction::RunCleanupsScope cleanups(cgf);
- auto &builder = cgf.getBuilder();
- auto scopeLoc = cgf.getLoc(e->getSourceRange());
+ mlir::Builder &builder = cgf.getBuilder();
+ mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
mlir::OpBuilder::InsertPoint scopeBegin;
+
+ // Explicitly introduce a scope for cleanup expressions, even though this
+ // overlaps with the RunCleanupsScope above.
+ //
+ // CIR does not yet model cleanup scopes explicitly, so a lexical scope is
+ // used as a temporary approximation. This is expected to be revisited once
+ // cleanup handling is redesigned.
cir::ScopeOp::create(builder, scopeLoc, /*scopeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
scopeBegin = b.saveInsertionPoint();
More information about the cfe-commits
mailing list