Skip to content

Commit 35d3b33

Browse files
[C++20][Coroutines] Lambda-coroutine with operator new in promise_type (llvm#84193)
Fix llvm#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 0456a32 commit 35d3b33

File tree

5 files changed

+169
-9
lines changed

5 files changed

+169
-9
lines changed

clang/include/clang/Sema/Sema.h

+10-2
Original file line numberDiff line numberDiff line change
@@ -6752,10 +6752,18 @@ class Sema final {
67526752
SourceLocation RParenLoc);
67536753

67546754
//// ActOnCXXThis - Parse 'this' pointer.
6755-
ExprResult ActOnCXXThis(SourceLocation loc);
6755+
///
6756+
/// \param ThisRefersToClosureObject Whether to skip the 'this' check for a
6757+
/// lambda because 'this' refers to the closure object.
6758+
ExprResult ActOnCXXThis(SourceLocation loc,
6759+
bool ThisRefersToClosureObject = false);
67566760

67576761
/// Build a CXXThisExpr and mark it referenced in the current context.
6758-
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit);
6762+
///
6763+
/// \param ThisRefersToClosureObject Whether to skip the 'this' check for a
6764+
/// lambda because 'this' refers to the closure object.
6765+
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
6766+
bool ThisRefersToClosureObject = false);
67596767
void MarkThisReferenced(CXXThisExpr *This);
67606768

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

clang/lib/Sema/SemaCoroutine.cpp

+16-2
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, /*ThisRefersToClosureObject=*/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

+11-5
Original file line numberDiff line numberDiff line change
@@ -1414,7 +1414,8 @@ 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,
1418+
bool ThisRefersToClosureObject) {
14181419
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
14191420
/// is a non-lvalue expression whose value is the address of the object for
14201421
/// which the function is called.
@@ -1434,13 +1435,18 @@ ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
14341435
return Diag(Loc, diag::err_invalid_this_use) << 0;
14351436
}
14361437

1437-
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
1438+
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false,
1439+
ThisRefersToClosureObject);
14381440
}
14391441

1440-
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,
1441-
bool IsImplicit) {
1442+
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit,
1443+
bool ThisRefersToClosureObject) {
14421444
auto *This = CXXThisExpr::Create(Context, Loc, Type, IsImplicit);
1443-
MarkThisReferenced(This);
1445+
1446+
if (!ThisRefersToClosureObject) {
1447+
MarkThisReferenced(This);
1448+
}
1449+
14441450
return This;
14451451
}
14461452

clang/test/SemaCXX/gh84064-1.cpp

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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+
struct CapturingThisTest
47+
{
48+
int x{};
49+
50+
void AsPointer()
51+
{
52+
auto lamb = [=,this]() -> Generator {
53+
int y = x;
54+
co_return;
55+
};
56+
57+
static_assert(sizeof(decltype(lamb)) == sizeof(void*));
58+
}
59+
60+
void AsStarThis()
61+
{
62+
auto lamb = [*this]() -> Generator {
63+
int y = x;
64+
co_return;
65+
};
66+
67+
static_assert(sizeof(decltype(lamb)) == sizeof(int));
68+
}
69+
};
70+
71+
int main()
72+
{
73+
auto lamb = []() -> Generator {
74+
co_return;
75+
};
76+
77+
static_assert(sizeof(decltype(lamb)) == 1);
78+
}
79+

clang/test/SemaCXX/gh84064-2.cpp

+53
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)