Skip to content

Commit 8f9336e

Browse files
committed
[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.
1 parent 52d5b8e commit 8f9336e

File tree

5 files changed

+143
-9
lines changed

5 files changed

+143
-9
lines changed

clang/include/clang/Sema/Sema.h

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6898,10 +6898,18 @@ class Sema final {
68986898
BinaryOperatorKind Operator);
68996899

69006900
//// ActOnCXXThis - Parse 'this' pointer.
6901-
ExprResult ActOnCXXThis(SourceLocation loc);
6901+
///
6902+
/// \param SkipLambdaCaptureCheck Whether to skip the 'this' check for a
6903+
/// lambda because 'this' is the lambda's 'this'-pointer.
6904+
ExprResult ActOnCXXThis(SourceLocation loc,
6905+
bool SkipLambdaCaptureCheck = false);
69026906

69036907
/// Build a CXXThisExpr and mark it referenced in the current context.
6904-
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit);
6908+
///
6909+
/// \param SkipLambdaCaptureCheck Whether to skip the 'this' check for a
6910+
/// lambda because 'this' is the lambda's 'this'-pointer.
6911+
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
6912+
bool SkipLambdaCaptureCheck = false);
69056913
void MarkThisReferenced(CXXThisExpr *This);
69066914

69076915
/// Try to retrieve the type of the 'this' pointer.

clang/lib/Sema/SemaCoroutine.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "clang/Sema/Initialization.h"
2626
#include "clang/Sema/Overload.h"
2727
#include "clang/Sema/ScopeInfo.h"
28+
#include "clang/Sema/Sema.h"
2829
#include "clang/Sema/SemaInternal.h"
2930
#include "llvm/ADT/SmallSet.h"
3031

@@ -1378,8 +1379,21 @@ bool CoroutineStmtBuilder::makeReturnOnAllocFailure() {
13781379
static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
13791380
SmallVectorImpl<Expr *> &PlacementArgs) {
13801381
if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) {
1381-
if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
1382-
ExprResult ThisExpr = S.ActOnCXXThis(Loc);
1382+
if (MD->isImplicitObjectMemberFunction()) {
1383+
ExprResult ThisExpr{};
1384+
1385+
if (isLambdaCallOperator(MD) && !MD->isStatic()) {
1386+
Qualifiers ThisQuals = MD->getMethodQualifiers();
1387+
CXXRecordDecl *Record = MD->getParent();
1388+
1389+
Sema::CXXThisScopeRAII ThisScope(S, Record, ThisQuals,
1390+
Record != nullptr);
1391+
1392+
ThisExpr = S.ActOnCXXThis(Loc, /*SkipLambdaCaptureCheck*/ true);
1393+
} else {
1394+
ThisExpr = S.ActOnCXXThis(Loc);
1395+
}
1396+
13831397
if (ThisExpr.isInvalid())
13841398
return false;
13851399
ThisExpr = S.CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,7 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
14141414
return false;
14151415
}
14161416

1417-
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
1417+
ExprResult Sema::ActOnCXXThis(SourceLocation Loc, bool SkipLambdaCaptureCheck) {
14181418
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
14191419
/// is a non-lvalue expression whose value is the address of the object for
14201420
/// which the function is called.
@@ -1434,13 +1434,18 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
14341434
return Diag(Loc, diag::err_invalid_this_use) << 0;
14351435
}
14361436

1437-
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
1437+
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false,
1438+
SkipLambdaCaptureCheck);
14381439
}
14391440

1440-
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
1441-
bool IsImplicit) {
1441+
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
1442+
bool SkipLambdaCaptureCheck) {
14421443
auto *This = CXXThisExpr::Create(Context, Loc, Type, IsImplicit);
1443-
MarkThisReferenced(This);
1444+
1445+
if (!SkipLambdaCaptureCheck) {
1446+
MarkThisReferenced(This);
1447+
}
1448+
14441449
return This;
14451450
}
14461451

clang/test/SemaCXX/gh84064-1.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -I%S/Inputs -std=c++20 %s
2+
3+
// expected-no-diagnostics
4+
5+
#include "std-coroutine.h"
6+
7+
using size_t = decltype(sizeof(0));
8+
9+
struct Generator {
10+
struct promise_type {
11+
int _val{};
12+
13+
Generator get_return_object() noexcept
14+
{
15+
return {};
16+
}
17+
18+
std::suspend_never initial_suspend() noexcept
19+
{
20+
return {};
21+
}
22+
23+
std::suspend_always final_suspend() noexcept
24+
{
25+
return {};
26+
}
27+
28+
void return_void() noexcept {}
29+
void unhandled_exception() noexcept {}
30+
31+
template<typename This, typename... TheRest>
32+
static void*
33+
operator new(size_t size,
34+
This&,
35+
TheRest&&...) noexcept
36+
{
37+
return nullptr;
38+
}
39+
40+
static void operator delete(void*, size_t)
41+
{
42+
}
43+
};
44+
};
45+
46+
int main()
47+
{
48+
auto lamb = []() -> Generator {
49+
co_return;
50+
};
51+
52+
static_assert(sizeof(decltype(lamb)) == 1);
53+
}
54+

clang/test/SemaCXX/gh84064-2.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// RUN: %clang_cc1 -fsyntax-only -verify -I%S/Inputs -std=c++23 %s
2+
3+
// expected-no-diagnostics
4+
5+
#include "std-coroutine.h"
6+
7+
using size_t = decltype(sizeof(0));
8+
9+
struct GeneratorStatic {
10+
struct promise_type {
11+
int _val{};
12+
13+
GeneratorStatic get_return_object() noexcept
14+
{
15+
return {};
16+
}
17+
18+
std::suspend_never initial_suspend() noexcept
19+
{
20+
return {};
21+
}
22+
23+
std::suspend_always final_suspend() noexcept
24+
{
25+
return {};
26+
}
27+
28+
void return_void() noexcept {}
29+
void unhandled_exception() noexcept {}
30+
31+
template<typename... TheRest>
32+
static void*
33+
operator new(size_t size,
34+
TheRest&&...) noexcept
35+
{
36+
return nullptr;
37+
}
38+
39+
static void operator delete(void*, size_t)
40+
{
41+
}
42+
};
43+
};
44+
45+
46+
int main()
47+
{
48+
auto lambCpp23 = []() static -> GeneratorStatic {
49+
co_return;
50+
};
51+
52+
static_assert(sizeof(decltype(lambCpp23)) == 1);
53+
}

0 commit comments

Comments
 (0)