Skip to content

Commit cd18342

Browse files
committed
[clang][Interp] Fix handling of generic lambdas
When compiling their static invoker, we need to get the right specialization.
1 parent 10cd0e7 commit cd18342

File tree

2 files changed

+42
-1
lines changed

2 files changed

+42
-1
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.cpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,34 @@ using namespace clang;
2323
using namespace clang::interp;
2424

2525
Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
26+
bool IsLambdaStaticInvoker = false;
27+
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
28+
MD && MD->isLambdaStaticInvoker()) {
29+
// For a lambda static invoker, we might have to pick a specialized
30+
// version if the lambda is generic. In that case, the picked function
31+
// will *NOT* be a static invoker anymore. However, it will still
32+
// be a non-static member function, this (usually) requiring an
33+
// instance pointer. We suppress that later in this function.
34+
IsLambdaStaticInvoker = true;
35+
36+
const CXXRecordDecl *ClosureClass = MD->getParent();
37+
assert(ClosureClass->captures_begin() == ClosureClass->captures_end());
38+
if (ClosureClass->isGenericLambda()) {
39+
const CXXMethodDecl *LambdaCallOp = ClosureClass->getLambdaCallOperator();
40+
assert(MD->isFunctionTemplateSpecialization() &&
41+
"A generic lambda's static-invoker function must be a "
42+
"template specialization");
43+
const TemplateArgumentList *TAL = MD->getTemplateSpecializationArgs();
44+
FunctionTemplateDecl *CallOpTemplate =
45+
LambdaCallOp->getDescribedFunctionTemplate();
46+
void *InsertPos = nullptr;
47+
const FunctionDecl *CorrespondingCallOpSpecialization =
48+
CallOpTemplate->findSpecialization(TAL->asArray(), InsertPos);
49+
assert(CorrespondingCallOpSpecialization);
50+
FuncDecl = cast<CXXMethodDecl>(CorrespondingCallOpSpecialization);
51+
}
52+
}
53+
2654
// Set up argument indices.
2755
unsigned ParamOffset = 0;
2856
SmallVector<PrimType, 8> ParamTypes;
@@ -46,7 +74,7 @@ Function *ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
4674
// InterpStack when calling the function.
4775
bool HasThisPointer = false;
4876
if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) {
49-
if (MD->isImplicitObjectMemberFunction()) {
77+
if (MD->isImplicitObjectMemberFunction() && !IsLambdaStaticInvoker) {
5078
HasThisPointer = true;
5179
ParamTypes.push_back(PT_Ptr);
5280
ParamOffsets.push_back(ParamOffset);

clang/test/AST/Interp/lambda.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,19 @@ namespace StaticInvoker {
155155
return fp(i).a;
156156
}
157157
static_assert(sv6(12) == 12);
158+
159+
160+
/// A generic lambda.
161+
auto GL = [](auto a) { return a; };
162+
constexpr char (*fp2)(char) = GL;
163+
static_assert(fp2('3') == '3', "");
164+
165+
struct GLS {
166+
int a;
167+
};
168+
auto GL2 = [](auto a) { return GLS{a}; };
169+
constexpr GLS (*fp3)(char) = GL2;
170+
static_assert(fp3('3').a == '3', "");
158171
}
159172

160173
namespace LambdasAsParams {

0 commit comments

Comments
 (0)