Skip to content

Commit

Permalink
Fixed #92: Added support to show coroutines as written.
Browse files Browse the repository at this point in the history
For more information see docs.cppinsights.io/transformations-coroutines.html.
  • Loading branch information
andreasfertig committed Nov 1, 2019
1 parent 0963154 commit e69e073
Show file tree
Hide file tree
Showing 10 changed files with 617 additions and 7 deletions.
37 changes: 37 additions & 0 deletions CodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,43 @@ void CodeGenerator::InsertTemplateGuardEnd(const FunctionDecl* stmt)
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const CoroutineBodyStmt* stmt)
{
InsertArg(stmt->getBody());
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const CoroutineSuspendExpr* stmt)
{
// co_await or co_yield
if(isa<CoyieldExpr>(stmt)) {
mOutputFormatHelper.Append("co_yield ");
} else {
mOutputFormatHelper.Append("co_await ");
}

// peal of __promise.yield_value
if(const auto* matTemp = dyn_cast_or_null<MaterializeTemporaryExpr>(stmt->getCommonExpr())) {
const auto* temporary = matTemp->getTemporary();

if(const auto* memExpr = dyn_cast_or_null<CXXMemberCallExpr>(temporary)) {
ForEachArg(memExpr->arguments(), [&](const auto& arg) { InsertArg(arg); });

// Seems to be the path for a co_await expr
} else {
InsertArg(temporary);
}
}
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const CoreturnStmt* stmt)
{
mOutputFormatHelper.Append("co_return ");
InsertArg(stmt->getOperand());
}
//-----------------------------------------------------------------------------

void CodeGenerator::InsertArg(const FunctionDecl* stmt)
{
// LAMBDA_SCOPE_HELPER(VarDecl);
Expand Down
3 changes: 3 additions & 0 deletions CodeGeneratorTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ SUPPORTED_STMT(CXXUnresolvedConstructExpr)
SUPPORTED_STMT(UnresolvedMemberExpr)
SUPPORTED_STMT(PackExpansionExpr)
SUPPORTED_STMT(CXXFoldExpr)
SUPPORTED_STMT(CoroutineBodyStmt)
SUPPORTED_STMT(CoroutineSuspendExpr)
SUPPORTED_STMT(CoreturnStmt)

#undef IGNORED_DECL
#undef IGNORED_STMT
Expand Down
5 changes: 3 additions & 2 deletions FunctionDeclHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ FunctionDeclHandler::FunctionDeclHandler(Rewriter& rewrite, MatchFinder& matcher
matcher.addMatcher(functionDecl(unless(anyOf(cxxMethodDecl(),
isExpansionInSystemHeader(),
isTemplate,
hasAncestor(friendDecl()), // friendDecl has functionDecl as child
hasAncestor(functionDecl()), // prevent forward declarations
hasParent(linkageSpecDecl()), // filter this out for coroutines
hasAncestor(friendDecl()), // friendDecl has functionDecl as child
hasAncestor(functionDecl()), // prevent forward declarations
isInvalidLocation())))
.bind("funcDecl"),
this);
Expand Down
3 changes: 3 additions & 0 deletions docs/Contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ code can be found on [GitHub](https://github.com) at [github.com/andreasfertig/c
* [Readme Docs](@ref readme_docs)
* [Readme Scripts](@ref readme_scripts)

##Transformations

* [Coroutines](@ref transformations-coroutines)
10 changes: 10 additions & 0 deletions docs/Coroutines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Coroutines {#transformations-coroutines}

[#92](https://github.com/andreasfertig/cppinsights/issues/92) asked for coroutines support, with the desire to see the
implemented FSM behind them. After investigating this issue for some time the conclusion is to show coroutines as
written. Without support for the implemented FSM behind them. There is a blog post which explains what needs to be done
to show the FSM behind coroutines: [Coroutines in C++ Insights](https://www.andreasfertig.blog/2019/09/coroutines-in-c-insights.html). A
following Twitter poll did not achieve enough confidence (for more information see here: [Coroutines in C++ Insights - The poll result](https://www.andreasfertig.blog/2019/10/coroutines-in-c-insights-the-poll-result.html)), that it is worth at this point to create a questionable implementation.

One thing to note is, at the moment only libc++ does support coroutines. You will need to enable using libc++ at the web
front end of [C++ Insights](https://cppinsights.io) to get rid of compiler errors.
88 changes: 88 additions & 0 deletions tests/CoroutinesCoReturnPlusTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// cmdline:-std=c++2a
#include <experimental/coroutine>

namespace stdx = std::experimental;

struct generator {
struct promise_type {
int current_value;
stdx::suspend_always yield_value(int value) {
this->current_value = value;
return {};
}

stdx::suspend_always initial_suspend() { return {}; }
stdx::suspend_always final_suspend() { return {}; }
generator get_return_object() { return generator{this}; };
void unhandled_exception() { std::terminate(); }
void return_value(int value) { }

// gives us getReturnStmtOnAllocFailure
static generator get_return_object_on_allocation_failure(){
throw std::bad_alloc();
}
};


// shortening the name
using coro_handle = stdx::coroutine_handle<promise_type>;

bool await_ready() { return false; }
void await_suspend(coro_handle waiter) {
waiter.resume();
}
auto await_resume() {
return p.promise().current_value;
}

generator(generator const&) = delete;
generator(generator &&rhs) : p(rhs.p) { rhs.p = nullptr; }

~generator() {
if (p)
p.destroy();
}

private:
explicit generator(promise_type *p)
: p(coro_handle::from_promise(*p)) {}

coro_handle p;
};

generator simpleReturn(int v ) {
co_return v;
}



generator additionAwaitReturn(int v ) {
// Here we look at an example, where __f->__promise.return_value( contains the two other coroutine expressions with
// a +. Backtracking is required.
// __f->__promise.return_value(__f->__promise_10_24 + __f->__promise_10_51);
co_return co_await simpleReturn(v) + co_await simpleReturn(v) + co_await simpleReturn(v+1);
}

generator awaitReturn(int v ) {
// Here we look at an example, where __f->__promise.return_value( contains the two other coroutine expressions with
// a +. Backtracking is required.
// __f->__promise.return_value(__f->__promise_10_24 + __f->__promise_10_51);
co_return co_await simpleReturn(v+41);
}

generator bracedReturn(int v ) {
// Here we look at an example, where __f->__promise.return_value( contains the two other coroutine expressions with
// a +. Backtracking is required.
// __f->__promise.return_value(__f->__promise_10_24 + __f->__promise_10_51);
co_return { v };
}

int main() {
auto sr = simpleReturn(3);

auto aar = additionAwaitReturn(2);

auto ar = awaitReturn(44);

auto br = bracedReturn(5);
}
124 changes: 124 additions & 0 deletions tests/CoroutinesCoReturnPlusTest.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
// cmdline:-std=c++2a
#include <experimental/coroutine>

namespace stdx = std::experimental;

struct generator
{
struct promise_type
{
int current_value;
inline stdx::suspend_always yield_value(int value)
{
this->current_value = value;
return {};
}

inline stdx::suspend_always initial_suspend()
{
return {};
}

inline stdx::suspend_always final_suspend()
{
return {};
}

inline generator get_return_object()
{
return generator{this};
}

inline void unhandled_exception()
{
std::terminate();
}

inline void return_value(int value)
{
}

static inline generator get_return_object_on_allocation_failure()
{
throw std::bad_alloc();
}

// inline promise_type() noexcept = default;
};

using coro_handle = stdx::coroutine_handle<generator::promise_type>;
inline bool await_ready()
{
return false;
}

inline void await_suspend(std::experimental::coroutine_handle<generator::promise_type> waiter)
{
static_cast<std::experimental::coroutine_handle<void>&>(waiter).resume();
}

inline int await_resume()
{
return this->p.promise().current_value;
}

// inline generator(const generator &) = delete;
inline generator(generator && rhs)
: p{std::experimental::coroutine_handle<generator::promise_type>(rhs.p)}
{
rhs.p.operator=(nullptr);
}

inline ~generator() noexcept
{
if(static_cast<bool>(static_cast<const std::experimental::coroutine_handle<void>&>(this->p).operator bool())) static_cast<std::experimental::coroutine_handle<void>&>(this->p).destroy();


}


private:
inline explicit generator(generator::promise_type * p)
: p{std::experimental::coroutine_handle<generator::promise_type>::from_promise(*p)}
{
}

std::experimental::coroutine_handle<generator::promise_type> p;
};



generator simpleReturn(int v)
{
co_return v;
}




generator additionAwaitReturn(int v)
{
co_return (co_await simpleReturn(v) + co_await simpleReturn(v)) + co_await simpleReturn(v + 1);
}


generator awaitReturn(int v)
{
co_return co_await simpleReturn(v + 41);
}


generator bracedReturn(int v)
{
co_return {v};
}


int main()
{
generator sr = simpleReturn(3);
generator aar = additionAwaitReturn(2);
generator ar = awaitReturn(44);
generator br = bracedReturn(5);
}

6 changes: 1 addition & 5 deletions tests/ExternCTest.expect
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
extern "C" void Foo();


int main()
{
Expand All @@ -8,8 +7,5 @@ int main()


extern "C" {
void Foo()
{
}

void Foo() {}
}
Loading

0 comments on commit e69e073

Please sign in to comment.