[clang] [clang] Fix couroutine error for operator new. (PR #215619)
Alina Sbirlea via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 11 23:15:43 PDT 2026
https://github.com/alinas updated https://github.com/llvm/llvm-project/pull/215619
>From ee7976ac94920e64e0212b630bfe00a31eee60b8 Mon Sep 17 00:00:00 2001
From: Alina Sbirlea <asbirlea at google.com>
Date: Tue, 11 Aug 2026 05:13:51 +0000
Subject: [PATCH 1/2] [clang] Fix couroutine error for operator new.
Fix error occuring with couroutines:
"error: too many arguments to function call, expected 3, have 4" for operator new.
Introduced by cd67cfecb1a1.
AI assisted explanation of the issue:
Prior to cd67cfecb1a1, Sema::FindAllocationFunctions took OperatorNew by reference and would unconditionally reset it to nullptr at the beginning of its search. The commit changed FindAllocationFunctions to return a std::optional<ResolvedAllocation>.
In clang/lib/Sema/SemaCoroutine.cpp, the result is handled like this:
1 IAP = ImplicitAllocationParameters(
2 alignedAllocationModeFromBool(ShouldUseAlignedAlloc));
3
4 auto FoundAllocations = S.FindAllocationFunctions(...);
5 if (FoundAllocations) {
6 IAP = FoundAllocations->IAP;
7 OperatorNew = FoundAllocations->OperatorNew;
8 }
If the coroutine promise defines an unaligned operator new(size_t, void*, size_t), the first lookup succeeds but is considered unaligned
(IAP.PassAlignment resolves to No). Because aligned allocation is enabled by default, the coroutine builder attempts a second lookup without placement
arguments:
1 if (!OperatorNew || (S.getLangOpts().CoroAlignedAllocation &&
2 !isAlignedAllocation(IAP.PassAlignment)))
3 LookupAllocationFunction(/*NewScope*/ AllocationFunctionScope::Class,
4 /*WithoutPlacementArgs*/ true);
When this second lookup runs, it resets IAP.PassAlignment to Yes at the start of the lambda. However, if this second lookup fails to find an aligned
operator new, FindAllocationFunctions returns std::nullopt.
Because FoundAllocations is empty, OperatorNew is never reset to nullptr, and IAP is never reset to No. They leak state:
- OperatorNew retains the result from the first successful lookup (which takes 3 parameters).
- IAP.PassAlignment retains the initialized Yes from the second failed lookup.
---
clang/lib/Sema/SemaCoroutine.cpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index aceb5f2aa33a4..48ee5cc0b0836 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -1488,6 +1488,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() {
if (FoundAllocations) {
IAP = FoundAllocations->IAP;
OperatorNew = FoundAllocations->OperatorNew;
+ } else {
+ OperatorNew = nullptr;
}
assert(!OperatorNew || !OperatorNew->isTypeAwareOperatorNewOrDelete());
};
>From 92f16920a71bc8c9fee44bb3b8da07c1b8d3360e Mon Sep 17 00:00:00 2001
From: Alina Sbirlea <asbirlea at google.com>
Date: Wed, 12 Aug 2026 05:47:53 +0000
Subject: [PATCH 2/2] Add test
---
clang/test/SemaCXX/coroutine-new-operator.cpp | 49 +++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 clang/test/SemaCXX/coroutine-new-operator.cpp
diff --git a/clang/test/SemaCXX/coroutine-new-operator.cpp b/clang/test/SemaCXX/coroutine-new-operator.cpp
new file mode 100644
index 0000000000000..b28b97fec1eda
--- /dev/null
+++ b/clang/test/SemaCXX/coroutine-new-operator.cpp
@@ -0,0 +1,49 @@
+// RUN: %clang_cc1 -std=c++20 -fcoro-aligned-allocation -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+namespace std {
+ template <typename R, typename... Args>
+ struct coroutine_traits {
+ using promise_type = typename R::promise_type;
+ };
+
+ template <class Promise = void> struct coroutine_handle {
+ coroutine_handle() = default;
+ static coroutine_handle from_address(void *) noexcept;
+ };
+ template <> struct coroutine_handle<void> {
+ static coroutine_handle from_address(void *) noexcept;
+ coroutine_handle() = default;
+ template <class Promise>
+ coroutine_handle(coroutine_handle<Promise>) noexcept;
+ };
+
+ struct suspend_always {
+ bool await_ready() const noexcept { return false; }
+ void await_suspend(coroutine_handle<>) const noexcept {}
+ void await_resume() const noexcept {}
+ };
+
+ enum class align_val_t : decltype(sizeof(0)) {};
+} // namespace std
+
+using size_t = decltype(sizeof(0));
+
+struct Task {
+ struct promise_type {
+ std::suspend_always initial_suspend() noexcept { return {}; }
+ std::suspend_always final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ Task get_return_object() noexcept { return {}; }
+
+ void* operator new(size_t n, void* buf, size_t capacity) noexcept {
+ return buf;
+ }
+ void operator delete(void*) noexcept {}
+ };
+};
+
+Task CoGetReturnAddress(void* buf, size_t capacity) {
+ co_return;
+}
More information about the cfe-commits
mailing list