Skip to content

Commit

Permalink
[clang][Interp] Fix handling of generic lambdas
Browse files Browse the repository at this point in the history
When compiling their static invoker, we need to get the
right specialization.
  • Loading branch information
tbaederr committed Feb 8, 2024
1 parent 10cd0e7 commit cd18342
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 1 deletion.
30 changes: 29 additions & 1 deletion clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,34 @@ using namespace clang;
using namespace clang::interp;

Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
bool IsLambdaStaticInvoker = false;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
MD && MD->isLambdaStaticInvoker()) {
// For a lambda static invoker, we might have to pick a specialized
// version if the lambda is generic. In that case, the picked function
// will *NOT* be a static invoker anymore. However, it will still
// be a non-static member function, this (usually) requiring an
// instance pointer. We suppress that later in this function.
IsLambdaStaticInvoker = true;

const CXXRecordDecl *ClosureClass = MD->getParent();
assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
if (ClosureClass->isGenericLambda()) {
const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
assert(MD->isFunctionTemplateSpecialization() &&
"A generic lambda's static-invoker function must be a "
"template specialization");
const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
FunctionTemplateDecl *CallOpTemplate =
LambdaCallOp->getDescribedFunctionTemplate();
void *InsertPos = nullptr;
const FunctionDecl *CorrespondingCallOpSpecialization =
CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
assert(CorrespondingCallOpSpecialization);
FuncDecl = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
}
}

// Set up argument indices.
unsigned ParamOffset = 0;
SmallVector<PrimType, 8> ParamTypes;
Expand All @@ -46,7 +74,7 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
// InterpStack when calling the function.
bool HasThisPointer = false;
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
if (MD->isImplicitObjectMemberFunction()) {
if (MD->isImplicitObjectMemberFunction() && !IsLambdaStaticInvoker) {
HasThisPointer = true;
ParamTypes.push_back(PT_Ptr);
ParamOffsets.push_back(ParamOffset);
Expand Down
13 changes: 13 additions & 0 deletions clang/test/AST/Interp/lambda.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,19 @@ namespace StaticInvoker {
return fp(i).a;
}
static_assert(sv6(12) == 12);


/// A generic lambda.
auto GL = [](auto a) { return a; };
constexpr char (*fp2)(char) = GL;
static_assert(fp2('3') == '3', "");

struct GLS {
int a;
};
auto GL2 = [](auto a) { return GLS{a}; };
constexpr GLS (*fp3)(char) = GL2;
static_assert(fp3('3').a == '3', "");
}

namespace LambdasAsParams {
Expand Down

0 comments on commit cd18342

Please sign in to comment.