[clang] [C++20][Coroutines] Lambda-coroutine with operator new in promise_type (PR #84193)
Andreas Fertig via cfe-commits
cfe-commits at lists.llvm.org
Wed Mar 6 08:35:21 PST 2024
https://github.com/andreasfertig created https://github.com/llvm/llvm-project/pull/84193
Fix #84064
According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first parameter for overload resolution of `operator new` is `size_t` followed by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first argument is the lvalue of `*this` if the coroutine is a member function.
Before this patch, Clang handled class types correctly but ignored lambdas. This patch adds support for lambda coroutines with a `promise_type` that implements a custom `operator new`.
The patch does consider C++23 `static operator()`, which already worked as there is no `this` parameter.
>From 8f9336e6c534b4ba71e3e490ebe95527529dc398 Mon Sep 17 00:00:00 2001
From: Andreas Fertig <andy at cppinsights.io>
Date: Wed, 6 Mar 2024 14:20:00 +0100
Subject: [PATCH] [C++20][Coroutines] Lambda-coroutine with operator new in
promise_type
Fix #84064
According to http://eel.is/c++draft/dcl.fct.def.coroutine#9 the first
parameter for overload resolution of `operator new` is `size_t` followed
by the arguments of the coroutine function.
http://eel.is/c++draft/dcl.fct.def.coroutine#4 states that the first argument
is the lvalue of `*this` if the coroutine is a member function.
Before this patch, Clang handled class types correctly but ignored lambdas.
This patch adds support for lambda coroutines with a `promise_type` that
implements a custom `operator new`.
The patch does consider C++23 `static operator()`, which already worked as
there is no `this` parameter.
---
clang/include/clang/Sema/Sema.h | 12 +++++--
clang/lib/Sema/SemaCoroutine.cpp | 18 +++++++++--
clang/lib/Sema/SemaExprCXX.cpp | 15 ++++++---
clang/test/SemaCXX/gh84064-1.cpp | 54 ++++++++++++++++++++++++++++++++
clang/test/SemaCXX/gh84064-2.cpp | 53 +++++++++++++++++++++++++++++++
5 files changed, 143 insertions(+), 9 deletions(-)
create mode 100644 clang/test/SemaCXX/gh84064-1.cpp
create mode 100644 clang/test/SemaCXX/gh84064-2.cpp
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 25c4c58ad4ae43..f6d81a15487a3e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6898,10 +6898,18 @@ class Sema final {
BinaryOperatorKind Operator);
//// ActOnCXXThis - Parse 'this' pointer.
- ExprResult ActOnCXXThis(SourceLocation loc);
+ ///
+ /// \param SkipLambdaCaptureCheck Whether to skip the 'this' check for a
+ /// lambda because 'this' is the lambda's 'this'-pointer.
+ ExprResult ActOnCXXThis(SourceLocation loc,
+ bool SkipLambdaCaptureCheck = false);
/// Build a CXXThisExpr and mark it referenced in the current context.
- Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit);
+ ///
+ /// \param SkipLambdaCaptureCheck Whether to skip the 'this' check for a
+ /// lambda because 'this' is the lambda's 'this'-pointer.
+ Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
+ bool SkipLambdaCaptureCheck = false);
void MarkThisReferenced(CXXThisExpr *This);
/// Try to retrieve the type of the 'this' pointer.
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index a969b9383563b2..d5655309d21f28 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -25,6 +25,7 @@
#include "clang/Sema/Initialization.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
#include "llvm/ADT/SmallSet.h"
@@ -1378,8 +1379,21 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
SmallVectorImpl<Expr *> &PlacementArgs) {
if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) {
- if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
- ExprResult ThisExpr = S.ActOnCXXThis(Loc);
+ if (MD->isImplicitObjectMemberFunction()) {
+ ExprResult ThisExpr{};
+
+ if (isLambdaCallOperator(MD) && !MD->isStatic()) {
+ Qualifiers ThisQuals = MD->getMethodQualifiers();
+ CXXRecordDecl *Record = MD->getParent();
+
+ Sema::CXXThisScopeRAII ThisScope(S, Record, ThisQuals,
+ Record != nullptr);
+
+ ThisExpr = S.ActOnCXXThis(Loc, /*SkipLambdaCaptureCheck*/ true);
+ } else {
+ ThisExpr = S.ActOnCXXThis(Loc);
+ }
+
if (ThisExpr.isInvalid())
return false;
ThisExpr = S.CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c34a40fa7c81ac..499c4943c23b01 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1414,7 +1414,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
return false;
}
-ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
+ExprResult Sema::ActOnCXXThis(SourceLocation Loc, bool SkipLambdaCaptureCheck) {
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
/// is a non-lvalue expression whose value is the address of the object for
/// which the function is called.
@@ -1434,13 +1434,18 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
return Diag(Loc, diag::err_invalid_this_use) << 0;
}
- return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
+ return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false,
+ SkipLambdaCaptureCheck);
}
-Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
- bool IsImplicit) {
+Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
+ bool SkipLambdaCaptureCheck) {
auto *This = CXXThisExpr::Create(Context, Loc, Type, IsImplicit);
- MarkThisReferenced(This);
+
+ if (!SkipLambdaCaptureCheck) {
+ MarkThisReferenced(This);
+ }
+
return This;
}
diff --git a/clang/test/SemaCXX/gh84064-1.cpp b/clang/test/SemaCXX/gh84064-1.cpp
new file mode 100644
index 00000000000000..dc7c475041094a
--- /dev/null
+++ b/clang/test/SemaCXX/gh84064-1.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -I%S/Inputs -std=c++20 %s
+
+// expected-no-diagnostics
+
+#include "std-coroutine.h"
+
+using size_t = decltype(sizeof(0));
+
+struct Generator {
+ struct promise_type {
+ int _val{};
+
+ Generator get_return_object() noexcept
+ {
+ return {};
+ }
+
+ std::suspend_never initial_suspend() noexcept
+ {
+ return {};
+ }
+
+ std::suspend_always final_suspend() noexcept
+ {
+ return {};
+ }
+
+ void return_void() noexcept {}
+ void unhandled_exception() noexcept {}
+
+ template<typename This, typename... TheRest>
+ static void*
+ operator new(size_t size,
+ This&,
+ TheRest&&...) noexcept
+ {
+ return nullptr;
+ }
+
+ static void operator delete(void*, size_t)
+ {
+ }
+ };
+};
+
+int main()
+{
+ auto lamb = []() -> Generator {
+ co_return;
+ };
+
+ static_assert(sizeof(decltype(lamb)) == 1);
+}
+
diff --git a/clang/test/SemaCXX/gh84064-2.cpp b/clang/test/SemaCXX/gh84064-2.cpp
new file mode 100644
index 00000000000000..457de43eab6d9e
--- /dev/null
+++ b/clang/test/SemaCXX/gh84064-2.cpp
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -I%S/Inputs -std=c++23 %s
+
+// expected-no-diagnostics
+
+#include "std-coroutine.h"
+
+using size_t = decltype(sizeof(0));
+
+struct GeneratorStatic {
+ struct promise_type {
+ int _val{};
+
+ GeneratorStatic get_return_object() noexcept
+ {
+ return {};
+ }
+
+ std::suspend_never initial_suspend() noexcept
+ {
+ return {};
+ }
+
+ std::suspend_always final_suspend() noexcept
+ {
+ return {};
+ }
+
+ void return_void() noexcept {}
+ void unhandled_exception() noexcept {}
+
+ template<typename... TheRest>
+ static void*
+ operator new(size_t size,
+ TheRest&&...) noexcept
+ {
+ return nullptr;
+ }
+
+ static void operator delete(void*, size_t)
+ {
+ }
+ };
+};
+
+
+int main()
+{
+ auto lambCpp23 = []() static -> GeneratorStatic {
+ co_return;
+ };
+
+ static_assert(sizeof(decltype(lambCpp23)) == 1);
+}
More information about the cfe-commits
mailing list