[llvm-branch-commits] [clang] 02a1978 - [CIR] Support CoawaitExpr in emitLValue (#232280)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Oct 22 09:47:58 PDT 2026
Author: Aman Maurya
Date: 2026-10-22T21:22:55+05:30
New Revision: 02a19785c91cd7bab502f84362f18bc53ac3caea
URL: https://github.com/llvm/llvm-project/commit/02a19785c91cd7bab502f84362f18bc53ac3caea
DIFF: https://github.com/llvm/llvm-project/commit/02a19785c91cd7bab502f84362f18bc53ac3caea.diff
LOG: [CIR] Support CoawaitExpr in emitLValue (#232280)
In `CIRGenFunction::emitLValue`, `Expr::CoawaitExprClass` previously
called `errorNYI`. When a coroutine awaits an expression whose
`await_resume()` returns an lvalue reference (for example, `int &x =
co_await awaiter;` or `(co_await awaiter) = 42;`), Clang evaluates the
`CoawaitExpr` as an lvalue.
This patch:
- Routes `Expr::CoawaitExprClass` to `emitCoawaitLValue` in
`CIRGenFunction::emitLValue`.
- Refactors `emitCoawaitLValue` and `emitCoyieldLValue` into a shared
`emitSuspendLValue` helper.
- Handles resume expressions in try/catch (where `tmpResumeLValAddr` is
null) by asserting `isResumeExprInTryCatch` and returning a null pointer
lvalue.
- Adds FileCheck tests in
`clang/test/CIR/CodeGenCoroutines/coro-await.cpp`, including test
coverage with C++ exceptions enabled.
Fixes #232232.
Added:
clang/test/CIR/CodeGenCoroutines/coro-await.cpp
Modified:
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.cpp
clang/lib/CIR/CodeGen/CIRGenFunction.h
Removed:
################################################################################
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 3ca0a29db565c1..41c1ab1abfcc56 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -822,7 +822,9 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
assert(awaitRes.lv.isSimple() &&
"expected simple lvalue for coroutine resume");
mlir::Value ptr = awaitRes.lv.getPointer();
- llvm::StringRef tmpName = "__coyield_resume_lval";
+ llvm::StringRef tmpName = kind == cir::AwaitKind::Yield
+ ? "__coyield_resume_lval"
+ : "__coawait_resume_lval";
tmpResumeRValAddr = cgf.emitAlloca(
tmpName, ptr.getType(), loc, cgf.getPointerAlign(),
builder.getBestAllocaInsertPoint(scopeParentBlock));
@@ -918,7 +920,6 @@ RValue CIRGenFunction::emitCoyieldExpr(const CoyieldExpr &e,
ignoreResult);
}
-#ifndef NDEBUG
static QualType
getCoroutineSuspendExprReturnType(const ASTContext &ctx,
const CoroutineSuspendExpr *e) {
@@ -926,31 +927,48 @@ getCoroutineSuspendExprReturnType(const ASTContext &ctx,
assert(isa<CallExpr>(re) && "unexpected suspend expression type");
return cast<CallExpr>(re)->getCallReturnType(ctx);
}
-#endif
-LValue CIRGenFunction::emitCoyieldLValue(const CoyieldExpr *e) {
- assert(
- getCoroutineSuspendExprReturnType(getContext(), e)->isReferenceType() &&
- "Can't have a scalar return unless the return type is a "
- "reference type!");
- mlir::Location scopeLoc = getLoc(e->getSourceRange());
- mlir::Block *currEntryBlock = curLexScope->getEntryBlock();
+static LValue emitSuspendLValue(CIRGenFunction &cgf,
+ const CoroutineSuspendExpr *e,
+ cir::AwaitKind kind) {
+ QualType retTy = getCoroutineSuspendExprReturnType(cgf.getContext(), e);
+ assert(retTy->isReferenceType() &&
+ "Can't have a scalar return unless the return type is a "
+ "reference type!");
+ mlir::Location scopeLoc = cgf.getLoc(e->getSourceRange());
+ mlir::Block *currEntryBlock = cgf.curLexScope->getEntryBlock();
mlir::Value tmpResumeLValAddr;
- LValue lv =
- emitSuspendExpression(*this, *curCoro.data, *e, cir::AwaitKind::Yield,
- AggValueSlot::ignored(),
- /*ignoreResult=*/false, currEntryBlock,
- tmpResumeLValAddr, /*forLValue=*/true)
- .lv;
- if (tmpResumeLValAddr) {
- Address addr = lv.getAddress();
- mlir::Value loadedPtr = cir::LoadOp::create(
- getBuilder(), scopeLoc, addr.getPointer().getType(), tmpResumeLValAddr);
- lv.setAddress(addr.withPointer(loadedPtr));
+ LValue lv = emitSuspendExpression(cgf, *cgf.curCoro.data, *e, kind,
+ AggValueSlot::ignored(),
+ /*ignoreResult=*/false, currEntryBlock,
+ tmpResumeLValAddr, /*forLValue=*/true)
+ .lv;
+ if (!tmpResumeLValAddr) {
+ assert(isResumeExprInTryCatch(*cgf.curCoro.data, kind, *e) &&
+ "expected resume in try/catch");
+ QualType pointeeTy = retTy->getPointeeType();
+ mlir::Type elemTy = cgf.convertType(pointeeTy);
+ mlir::Value nullPtr = cgf.getBuilder().getNullPtr(
+ cgf.getBuilder().getPointerTo(elemTy), scopeLoc);
+ return cgf.makeNaturalAlignAddrLValue(nullPtr, pointeeTy);
}
+
+ Address addr = lv.getAddress();
+ mlir::Value loadedPtr =
+ cir::LoadOp::create(cgf.getBuilder(), scopeLoc,
+ addr.getPointer().getType(), tmpResumeLValAddr);
+ lv.setAddress(addr.withPointer(loadedPtr));
return lv;
}
+LValue CIRGenFunction::emitCoawaitLValue(const CoawaitExpr *e) {
+ return emitSuspendLValue(*this, e, curCoro.data->currentAwaitKind);
+}
+
+LValue CIRGenFunction::emitCoyieldLValue(const CoyieldExpr *e) {
+ return emitSuspendLValue(*this, e, cir::AwaitKind::Yield);
+}
+
mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
++curCoro.data->coreturnCount;
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index e5f20f7dec01d1..409a94b9acff8f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1373,8 +1373,7 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
case Expr::MaterializeTemporaryExprClass:
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
case Expr::CoawaitExprClass:
- getCIRGenModule().errorNYI(e->getSourceRange(), "emitLValue: CoawaitExpr");
- return LValue();
+ return emitCoawaitLValue(cast<CoawaitExpr>(e));
case Expr::CoyieldExprClass:
return emitCoyieldLValue(cast<CoyieldExpr>(e));
case Expr::PackIndexingExprClass:
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index a143483d49391e..b97b0fe1f700c1 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -2316,6 +2316,7 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCoawaitExpr(const CoawaitExpr &e,
AggValueSlot aggSlot = AggValueSlot::ignored(),
bool ignoreResult = false);
+ LValue emitCoawaitLValue(const CoawaitExpr *e);
RValue emitCoyieldExpr(const CoyieldExpr &e,
AggValueSlot aggSlot = AggValueSlot::ignored(),
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-await.cpp b/clang/test/CIR/CodeGenCoroutines/coro-await.cpp
new file mode 100644
index 00000000000000..4cd3bb9a9df454
--- /dev/null
+++ b/clang/test/CIR/CodeGenCoroutines/coro-await.cpp
@@ -0,0 +1,75 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -fcxx-exceptions -fexceptions -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.eh.cir
+// RUN: FileCheck --input-file=%t.eh.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fcxx-exceptions -fexceptions -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.eh.ll
+// RUN: FileCheck --input-file=%t.eh.ll %s -check-prefix=OGCG
+
+#include "Inputs/coroutine.h"
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() { return {}; }
+ std::suspend_never initial_suspend() noexcept { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+struct Awaiter {
+ bool await_ready() { return false; }
+ void await_suspend(std::coroutine_handle<>) {}
+ int &await_resume() noexcept { return value; }
+ int value;
+};
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z17test_await_assignv
+// OGCG-LABEL: define dso_local void @_Z17test_await_assignv
+Task test_await_assign() {
+ // CIR: %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
+ // CIR: cir.await(user, ready : {
+ // CIR: %[[READY:.*]] = cir.call @_ZN7Awaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[READY]])
+ // CIR: }, suspend : {
+ // CIR: cir.call @_ZN7Awaiter13await_suspendESt16coroutine_handleIvE(%{{.*}})
+ // CIR: cir.coro.suspend_point
+ // CIR: }, resume : {
+ // CIR: %[[RES:.*]] = cir.call @_ZN7Awaiter12await_resumeEv(%{{.*}}) nothrow : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.ptr<!s32i>{{.*}})
+ // CIR: cir.store %[[RES]], %[[TMP_ADDR:.*]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+ // CIR: cir.yield
+ // CIR: },)
+ // CIR: %[[LOADED_PTR:.*]] = cir.load %[[TMP_ADDR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+ // CIR: cir.store align(4) %[[VAL]], %[[LOADED_PTR]] : !s32i, !cir.ptr<!s32i>
+
+ // OGCG: %[[RES_OGCG:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN7Awaiter12await_resumeEv(ptr {{.*}})
+ // OGCG: store i32 42, ptr %[[RES_OGCG]], align 4
+ Awaiter a;
+ (co_await a) = 42;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z14test_await_refv
+// OGCG-LABEL: define dso_local void @_Z14test_await_refv
+Task test_await_ref() {
+ // CIR: %[[R_ADDR:.*]] = cir.alloca "r" align(8) init const : !cir.ptr<!cir.ptr<!s32i>>
+ // CIR: cir.await(user, ready : {
+ // CIR: %[[READY:.*]] = cir.call @_ZN7Awaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[READY]])
+ // CIR: }, suspend : {
+ // CIR: cir.call @_ZN7Awaiter13await_suspendESt16coroutine_handleIvE(%{{.*}})
+ // CIR: cir.coro.suspend_point
+ // CIR: }, resume : {
+ // CIR: %[[RES:.*]] = cir.call @_ZN7Awaiter12await_resumeEv(%{{.*}}) nothrow : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.ptr<!s32i>{{.*}})
+ // CIR: cir.store %[[RES]], %[[TMP_ADDR:.*]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+ // CIR: cir.yield
+ // CIR: },)
+ // CIR: %[[LOADED_PTR:.*]] = cir.load %[[TMP_ADDR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i>
+ // CIR: cir.store align(8) %[[LOADED_PTR]], %[[R_ADDR]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>>
+
+ // OGCG: %[[RES_OGCG:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN7Awaiter12await_resumeEv(ptr {{.*}})
+ // OGCG: store ptr %[[RES_OGCG]], ptr %{{.*}}, align 8
+ Awaiter a;
+ int &r = co_await a;
+}
More information about the llvm-branch-commits
mailing list