Skip to content

Commit

Permalink
Fixed support for coroutine templates and C++20s struct as NTTP.
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasfertig committed Jan 28, 2025
1 parent cb44b7d commit e8056f5
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 11 deletions.
2 changes: 1 addition & 1 deletion CodeGenerator.h
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ class CoroutinesCodeGenerator final : public CodeGenerator
std::string mFSMName{};
CoroutineASTData mASTData{};
llvm::DenseMap<const Stmt*, bool> mBinaryExprs{};
static inline llvm::DenseMap<const Expr*, std::string>
static inline llvm::DenseMap<const Expr*, std::pair<const DeclRefExpr*, std::string>>
mOpaqueValues{}; ///! Keeps track of the current set of opaque value

QualType GetFrameType() const { return QualType(mASTData.mFrameType->getTypeForDecl(), 0); }
Expand Down
23 changes: 14 additions & 9 deletions CoroutinesCodeGenerator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,12 +431,12 @@ void CoroutinesCodeGenerator::InsertCoroutine(const FunctionDecl& fd, const Coro
}
}

auto& str = ofm.GetString();
auto str = std::move(ofm.GetString());
ReplaceAll(str, "<"sv, ""sv);
ReplaceAll(str, ":"sv, ""sv);
ReplaceAll(str, ">"sv, ""sv);
ReplaceAll(str, ","sv, ""sv);
ReplaceAll(str, " "sv, ""sv);

str = BuildTemplateParamObjectName(str);

if(fd.isOverloadedOperator()) {
return StrCat(MakeLineColumnName(ctx.getSourceManager(), stmt->getBeginLoc(), "operator_"sv), str);
Expand Down Expand Up @@ -737,7 +737,6 @@ void CoroutinesCodeGenerator::InsertArg(const CoroutineBodyStmt* stmt)
}

if(const auto* coReturnVoid = dyn_cast_or_null<CoreturnStmt>(stmt->getFallthroughHandler())) {
coReturnVoid->dump();
funcBodyStmts.Add(coReturnVoid);
}

Expand Down Expand Up @@ -817,10 +816,11 @@ void CoroutinesCodeGenerator::InsertArg(const CallExpr* stmt)
}
//-----------------------------------------------------------------------------

static std::optional<std::string> FindValue(llvm::DenseMap<const Expr*, std::string>& map, const Expr* key)
static std::optional<std::string>
FindValue(llvm::DenseMap<const Expr*, std::pair<const DeclRefExpr*, std::string>>& map, const Expr* key)
{
if(const auto& s = map.find(key); s != map.end()) {
return s->second;
return s->second.second;
}

return {};
Expand All @@ -838,18 +838,23 @@ void CoroutinesCodeGenerator::InsertArg(const OpaqueValueExpr* stmt)
// Needs to be internal because a user can create the same type and it gets put into the stack frame
std::string name{BuildSuspendVarName(stmt)};

// In case of a coroutine-template the same suspension point can occur multiple times. But to know when to add
// the _1 we must match the one from each instantiation. The DeclRefExpr is what distinguishes the same
// OpaqueValueExpr between multiple instantiations.
const auto* dref = FindDeclRef(sourceExpr);

// The initial_suspend and final_suspend expressions carry the same location info. If we hit such a case,
// make up another name.
// Below is a std::find_if. However, the same code looks unreadable with std::find_if
for(const auto lookupName{StrCat(CORO_FRAME_ACCESS, name)}; const auto& [k, v] : mOpaqueValues) {
if(v == lookupName) {
for(const auto lookupName{StrCat(CORO_FRAME_ACCESS, name)}; const auto& [k, value] : mOpaqueValues) {
if(auto [thisDeref, v] = value; (thisDeref == dref) and (v == lookupName)) {
name += "_1"sv;
break;
}
}

const auto accessName{StrCat(CORO_FRAME_ACCESS, name)};
mOpaqueValues.insert(std::make_pair(sourceExpr, accessName));
mOpaqueValues.insert(std::make_pair(sourceExpr, std::make_pair(dref, accessName)));

OutputFormatHelper ofm{};
CoroutinesCodeGenerator codeGenerator{ofm, mPosBeforeFunc, mFSMName, mSuspendsCount, mASTData};
Expand Down
1 change: 1 addition & 0 deletions InsightsHelpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,7 @@ std::string BuildTemplateParamObjectName(std::string name)
ReplaceAll(name, ","sv, "_"sv);
ReplaceAll(name, "."sv, "_"sv);
ReplaceAll(name, "+"sv, "_"sv);
ReplaceAll(name, "-"sv, "n"sv);

return name;
}
Expand Down
2 changes: 1 addition & 1 deletion InsightsStrCat.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ inline std::string Normalize(const APValue& arg)
::llvm::raw_string_ostream stream{str};

arg.getFloat().print(stream);
str.pop_back();

if(std::string::npos == str.find('.')) {
/* in case it is a number like 10.0 toString() seems to leave out the .0. However, as this distinguished
* between an integer and a floating point literal we need that dot. */
str.pop_back();
str.append(".0");
}

Expand Down
50 changes: 50 additions & 0 deletions tests/EduCoroutineFunctionTemplateTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// cmdline:-std=c++20
// cmdlineinsights:-edu-show-coroutine-transformation

#include <coroutine>
#include <exception> // std::terminate
#include <new>
#include <utility>

#define INSIGHTS_USE_TEMPLATE 1

struct ClsAsNTTPToCoro
{
int x;
double d;
};

template <typename T> struct generator {
struct promise_type {
T current_value{};

std::suspend_always yield_value(T value) {
current_value = value;
return {};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
generator get_return_object() { return generator{this}; };
void unhandled_exception() { std::terminate(); }
void return_value(T v) { current_value = v; }
};

generator(generator &&rhs) : p{std::exchange(rhs.p, nullptr)} {}
~generator() { if (p) { p.destroy(); } }

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

std::coroutine_handle<promise_type> p;
};

template <typename T, typename U, typename V, auto>
generator<T> fun() {
co_return 2;
}

int main() {
auto dbl = fun<int, char, unsigned int, 3.14>();
auto stct = fun<int, char, unsigned int, ClsAsNTTPToCoro{4, -3.0}>();
}
Loading

0 comments on commit e8056f5

Please sign in to comment.