https://github.com/Andres-Salamanca created 
https://github.com/llvm/llvm-project/pull/173174

 This PR adds support for returning the result of a `co_await` via `co_return`. 
A new variable, `__coawait_resume_rval`, is introduced to store the returned 
value.

>From 37736e80e0069479c5245cc9a2e99fc0b869f6b3 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <[email protected]>
Date: Sat, 20 Dec 2025 18:56:53 -0500
Subject: [PATCH] [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

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to