Skip to content

Commit

Permalink
[C++20][Coroutines] lambda-coroutine with promise_type ctor.
Browse files Browse the repository at this point in the history
This is a follow-up of llvm#84064. It turned out that a coroutine-lambda
with a `promise_type` and a user-defined constructor ignores the `this`
pointer. Per http://eel.is/c++draft/dcl.fct.def.coroutine#4, in such a
case, the first parameter to the constructor is an lvalue of `*this`.
  • Loading branch information
andreasfertig committed Mar 11, 2024
1 parent c54e052 commit cdeb381
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 3 deletions.
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,10 @@ C++20 Feature Support
templates (`P1814R0 <https://wg21.link/p1814r0>`_).
(#GH54051).

- Clang now treats a lambda-coroutine with a `promise_type` with a constructor
or a user-defined `operator new` correctly, passing the lambdas
`this`-pointer as the first argument of the parameter list.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^

Expand Down
19 changes: 16 additions & 3 deletions clang/lib/Sema/SemaCoroutine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -596,8 +596,21 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) {

// Add implicit object parameter.
if (auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
if (MD->isImplicitObjectMemberFunction() && !isLambdaCallOperator(MD)) {
ExprResult ThisExpr = ActOnCXXThis(Loc);
if (MD->isImplicitObjectMemberFunction()) {
ExprResult ThisExpr{};

if (isLambdaCallOperator(MD)) {
Qualifiers ThisQuals = MD->getMethodQualifiers();
CXXRecordDecl *Record = MD->getParent();

Sema::CXXThisScopeRAII ThisScope(*this, Record, ThisQuals,
/*Enabled=*/Record != nullptr);

ThisExpr = ActOnCXXThis(Loc, /*ThisRefersToClosureObject=*/true);
} else {
ThisExpr = ActOnCXXThis(Loc);
}

if (ThisExpr.isInvalid())
return nullptr;
ThisExpr = CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get());
Expand Down Expand Up @@ -1387,7 +1400,7 @@ static bool collectPlacementArgs(Sema &S, FunctionDecl &FD, SourceLocation Loc,
CXXRecordDecl *Record = MD->getParent();

Sema::CXXThisScopeRAII ThisScope(S, Record, ThisQuals,
Record != nullptr);
/*Enabled=*/Record != nullptr);

ThisExpr = S.ActOnCXXThis(Loc, /*ThisRefersToClosureObject=*/true);
} else {
Expand Down
71 changes: 71 additions & 0 deletions clang/test/SemaCXX/coroutine-promise-ctor-lambda.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// 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>
promise_type(This&,
TheRest&&...)
{
}
};
};

struct CapturingThisTest
{
int x{};

void AsPointer()
{
auto lamb = [=,this]() -> Generator {
int y = x;
co_return;
};

static_assert(sizeof(decltype(lamb)) == sizeof(void*));
}

void AsStarThis()
{
auto lamb = [*this]() -> Generator {
int y = x;
co_return;
};

static_assert(sizeof(decltype(lamb)) == sizeof(int));
}
};

int main()
{
auto lamb = []() -> Generator {
co_return;
};

static_assert(sizeof(decltype(lamb)) == 1);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// 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 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... TheRest>
promise_type(TheRest&&...)
{
}
};
};


int main()
{
auto lamb = []() static -> Generator {
co_return;
};

static_assert(sizeof(decltype(lamb)) == 1);
}

0 comments on commit cdeb381

Please sign in to comment.