Skip to content

Commit 36a88cf

Browse files
authored
Translate generic lambdas (#499)
1 parent 6b21d14 commit 36a88cf

24 files changed

Lines changed: 1278 additions & 65 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 45 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ bool Converter::VisitBuiltinType(clang::BuiltinType *type) {
170170
bool Converter::VisitRecordType(clang::RecordType *type) {
171171
auto *decl = type->getDecl();
172172
if (auto lambda = AsLambdaClass(clang::QualType(type, 0))) {
173+
if (lambda->isGenericLambda()) {
174+
StrCat("FnPtr<Generic>");
175+
return false;
176+
}
173177
auto proto = lambda->getLambdaCallOperator()
174178
->getType()
175179
->castAs<clang::FunctionProtoType>();
@@ -1877,7 +1881,8 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
18771881
expr->getCalleeDecl());
18781882
conversion && conversion->getParent()->isLambda()) {
18791883
ConvertLambdaToFunctionPointer(clang::cast<clang::CXXMemberCallExpr>(expr)
1880-
->getImplicitObjectArgument());
1884+
->getImplicitObjectArgument(),
1885+
conversion);
18811886
return false;
18821887
}
18831888

@@ -1958,16 +1963,31 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
19581963
return false;
19591964
}
19601965

1961-
void Converter::ConvertLambdaToFunctionPointer(clang::Expr *lambda) {
1966+
void Converter::ConvertLambdaToFunctionPointer(
1967+
clang::Expr *lambda, const clang::CXXConversionDecl *conversion) {
19621968
StrCat("Some");
19631969
PushParen paren(*this);
1964-
ConvertLambdaClosure(AsLambdaClass(lambda->getType()));
1970+
ConvertLambdaClosure(AsLambdaClass(lambda->getType()),
1971+
AsLambdaCallOperator(conversion));
19651972
computed_expr_type_ = ComputedExprType::FreshPointer;
19661973
}
19671974

1968-
void Converter::EmitFnPtrCall(clang::Expr *callee) {
1969-
if (AsLambdaClass(callee->getType())) {
1970-
StrCat(ConvertRValue(callee), ".call");
1975+
void Converter::ConvertLambdaCall(clang::CallExpr *expr,
1976+
const clang::CXXRecordDecl *lambda) {
1977+
StrCat(ConvertRValue(GetCallee(expr)));
1978+
if (lambda->isGenericLambda()) {
1979+
auto call_operator = expr->getDirectCallee();
1980+
auto proto = call_operator->getType()->castAs<clang::FunctionProtoType>();
1981+
StrCat(std::format(".spec::<{}>({})", ConvertFunctionPointerType(proto),
1982+
GetLambdaSpecializationIndex(call_operator)));
1983+
}
1984+
StrCat(".call");
1985+
}
1986+
1987+
void Converter::EmitFnPtrCall(clang::CallExpr *expr) {
1988+
auto callee = GetCallee(expr);
1989+
if (auto lambda = AsLambdaClass(callee->getType())) {
1990+
ConvertLambdaCall(expr, lambda);
19711991
return;
19721992
}
19731993
{
@@ -2014,8 +2034,9 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
20142034
auto callee = GetCallee(expr);
20152035
unsigned arg_begin = 0;
20162036
if (auto op_call = llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
2017-
if (clang::isa_and_nonnull<clang::CXXMethodDecl>(
2018-
op_call->getDirectCallee())) {
2037+
if (auto method = clang::dyn_cast_or_null<clang::CXXMethodDecl>(
2038+
op_call->getDirectCallee());
2039+
method && !method->isExplicitObjectMemberFunction()) {
20192040
arg_begin = 1;
20202041
}
20212042
}
@@ -2221,7 +2242,7 @@ void Converter::EmitCall(CallInfo &&info) {
22212242
EmitHoistedArgs(info);
22222243

22232244
if (info.is_fn_ptr_call) {
2224-
EmitFnPtrCall(GetCallee(info.expr));
2245+
EmitFnPtrCall(info.expr);
22252246
} else if (info.is_libc_passthrough) {
22262247
auto *direct_callee = info.expr->getDirectCallee();
22272248
assert(direct_callee);
@@ -3926,32 +3947,37 @@ bool Converter::VisitConstantExpr(clang::ConstantExpr *expr) {
39263947

39273948
bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
39283949
auto decl = expr->getLambdaClass();
3929-
if (decl->captures().empty()) {
3950+
if (decl->captures().empty() && !decl->isGenericLambda()) {
39303951
ConvertCapturelessLambda(decl);
39313952
return false;
39323953
}
39333954
StrCat(LambdaMacro());
39343955
PushParen paren(*this);
3956+
if (decl->isGenericLambda()) {
3957+
StrCat("Generic", token::kComma);
3958+
}
39353959
{
39363960
PushBrace captures(*this);
39373961
auto init = expr->capture_init_begin();
39383962
for (auto field : decl->fields()) {
39393963
ConvertLambdaCapture(field, *init++);
39403964
}
39413965
}
3942-
StrCat(token::kComma);
3943-
ConvertLambdaClosure(decl);
3966+
ForEachLambdaCallOperator(decl, [&](clang::CXXMethodDecl *call_operator) {
3967+
StrCat(token::kComma);
3968+
ConvertLambdaClosure(decl, call_operator);
3969+
});
39443970
computed_expr_type_ = ComputedExprType::FreshValue;
39453971
return false;
39463972
}
39473973

39483974
void Converter::ConvertCapturelessLambda(const clang::CXXRecordDecl *decl) {
3949-
auto proto = decl->getLambdaCallOperator()
3950-
->getType()
3951-
->castAs<clang::FunctionProtoType>();
3975+
assert(!decl->isGenericLambda());
3976+
auto call_operator = decl->getLambdaCallOperator();
3977+
auto proto = call_operator->getType()->castAs<clang::FunctionProtoType>();
39523978
StrCat(std::format("FnPtr::<{}>::new", ConvertFunctionPointerType(proto)));
39533979
PushParen paren(*this);
3954-
ConvertLambdaClosure(decl);
3980+
ConvertLambdaClosure(decl, call_operator);
39553981
computed_expr_type_ = ComputedExprType::FreshValue;
39563982
}
39573983

@@ -3964,8 +3990,8 @@ void Converter::ConvertLambdaCapture(const clang::FieldDecl *field,
39643990
StrCat(token::kSemiColon);
39653991
}
39663992

3967-
void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl) {
3968-
auto call_operator = decl->getLambdaCallOperator();
3993+
void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl,
3994+
clang::CXXMethodDecl *call_operator) {
39693995
StrCat('|');
39703996
for (auto p : call_operator->parameters()) {
39713997
StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()),
@@ -3975,7 +4001,7 @@ void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl) {
39754001
ConvertFunctionReturnType(call_operator);
39764002
PushBrace body(*this);
39774003
std::optional<PushBrace> unsafe_brace;
3978-
if (decl->captures().empty()) {
4004+
if (decl->captures().empty() && !decl->isGenericLambda()) {
39794005
StrCat(keyword_unsafe_);
39804006
unsafe_brace.emplace(*this);
39814007
}

‎cpp2rust/converter/converter.h‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,9 +329,14 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
329329

330330
void ConvertGenericCallExpr(clang::CallExpr *expr);
331331

332-
virtual void EmitFnPtrCall(clang::Expr *callee);
332+
virtual void EmitFnPtrCall(clang::CallExpr *expr);
333333

334-
virtual void ConvertLambdaToFunctionPointer(clang::Expr *lambda);
334+
void ConvertLambdaCall(clang::CallExpr *expr,
335+
const clang::CXXRecordDecl *lambda);
336+
337+
virtual void
338+
ConvertLambdaToFunctionPointer(clang::Expr *lambda,
339+
const clang::CXXConversionDecl *conversion);
335340

336341
virtual void
337342
ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl);
@@ -479,7 +484,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
479484
virtual void ConvertLambdaCapture(const clang::FieldDecl *field,
480485
clang::Expr *init);
481486

482-
void ConvertLambdaClosure(const clang::CXXRecordDecl *decl);
487+
void ConvertLambdaClosure(const clang::CXXRecordDecl *decl,
488+
clang::CXXMethodDecl *call_operator);
483489

484490
virtual bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr);
485491
virtual bool VisitCXXScalarValueInitExpr(clang::CXXScalarValueInitExpr *expr);

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,58 @@ AsLambdaOperatorCall(const clang::FunctionDecl *fn) {
11741174
return method;
11751175
}
11761176

1177+
void ForEachLambdaCallOperator(
1178+
const clang::CXXRecordDecl *decl,
1179+
llvm::function_ref<void(clang::CXXMethodDecl *)> fn) {
1180+
if (!decl->isGenericLambda()) {
1181+
fn(decl->getLambdaCallOperator());
1182+
return;
1183+
}
1184+
for (auto spec : decl->getDependentLambdaCallOperator()->specializations()) {
1185+
fn(clang::cast<clang::CXXMethodDecl>(spec));
1186+
}
1187+
}
1188+
1189+
clang::CXXMethodDecl *
1190+
AsLambdaCallOperator(const clang::CXXConversionDecl *conversion) {
1191+
auto decl = conversion->getParent();
1192+
if (!decl->isGenericLambda()) {
1193+
return decl->getLambdaCallOperator();
1194+
}
1195+
auto args = conversion->getTemplateSpecializationArgs()->asArray();
1196+
clang::CXXMethodDecl *found = nullptr;
1197+
ForEachLambdaCallOperator(decl, [&](clang::CXXMethodDecl *op) {
1198+
auto op_args = op->getTemplateSpecializationArgs()->asArray();
1199+
if (llvm::equal(args, op_args,
1200+
[](const clang::TemplateArgument &a,
1201+
const clang::TemplateArgument &b) {
1202+
return a.structurallyEquals(b);
1203+
})) {
1204+
found = op;
1205+
}
1206+
});
1207+
if (!found) {
1208+
llvm::report_fatal_error("no call operator matches the lambda conversion");
1209+
}
1210+
return found;
1211+
}
1212+
1213+
unsigned
1214+
GetLambdaSpecializationIndex(const clang::FunctionDecl *call_operator) {
1215+
std::optional<unsigned> index;
1216+
unsigned i = 0;
1217+
ForEachLambdaCallOperator(
1218+
clang::cast<clang::CXXMethodDecl>(call_operator)->getParent(),
1219+
[&](const clang::CXXMethodDecl *op) {
1220+
if (op->getCanonicalDecl() == call_operator->getCanonicalDecl()) {
1221+
index = i;
1222+
}
1223+
++i;
1224+
});
1225+
assert(index);
1226+
return *index;
1227+
}
1228+
11771229
const clang::LambdaCapture *AsLambdaCapture(const clang::FieldDecl *field) {
11781230
auto decl = clang::dyn_cast<clang::CXXRecordDecl>(field->getParent());
11791231
if (!decl || !decl->isLambda()) {
@@ -1689,6 +1741,10 @@ clang::Expr *GetCallee(clang::CallExpr *expr) {
16891741
return op_call->getArg(0);
16901742
}
16911743
}
1744+
if (auto member_call = clang::dyn_cast<clang::CXXMemberCallExpr>(expr);
1745+
member_call && AsLambdaOperatorCall(member_call->getMethodDecl())) {
1746+
return member_call->getImplicitObjectArgument();
1747+
}
16921748
return expr->getCallee();
16931749
}
16941750

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,15 @@ const clang::CXXRecordDecl *AsLambdaClass(clang::QualType type);
214214

215215
const clang::CXXMethodDecl *AsLambdaOperatorCall(const clang::FunctionDecl *fn);
216216

217+
void ForEachLambdaCallOperator(
218+
const clang::CXXRecordDecl *decl,
219+
llvm::function_ref<void(clang::CXXMethodDecl *)> fn);
220+
221+
clang::CXXMethodDecl *
222+
AsLambdaCallOperator(const clang::CXXConversionDecl *conversion);
223+
224+
unsigned GetLambdaSpecializationIndex(const clang::FunctionDecl *call_operator);
225+
217226
const clang::LambdaCapture *AsLambdaCapture(const clang::FieldDecl *field);
218227

219228
bool IsLambdaCopyOrMoveConstructor(const clang::CXXConstructorDecl *ctor);

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,8 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
13111311
expr->getCalleeDecl());
13121312
conversion && conversion->getParent()->isLambda()) {
13131313
ConvertLambdaToFunctionPointer(clang::cast<clang::CXXMemberCallExpr>(expr)
1314-
->getImplicitObjectArgument());
1314+
->getImplicitObjectArgument(),
1315+
conversion);
13151316
return false;
13161317
}
13171318

@@ -1546,9 +1547,10 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
15461547
return Converter::VisitImplicitCastExpr(expr);
15471548
}
15481549

1549-
void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) {
1550-
if (AsLambdaClass(callee->getType())) {
1551-
StrCat(ConvertRValue(callee), ".call");
1550+
void ConverterRefCount::EmitFnPtrCall(clang::CallExpr *expr) {
1551+
auto callee = GetCallee(expr);
1552+
if (auto lambda = AsLambdaClass(callee->getType())) {
1553+
ConvertLambdaCall(expr, lambda);
15521554
return;
15531555
}
15541556
Convert(callee);
@@ -1572,8 +1574,16 @@ void ConverterRefCount::ConvertLambdaCapture(const clang::FieldDecl *field,
15721574
Converter::ConvertLambdaCapture(field, init);
15731575
}
15741576

1575-
void ConverterRefCount::ConvertLambdaToFunctionPointer(clang::Expr *lambda) {
1577+
void ConverterRefCount::ConvertLambdaToFunctionPointer(
1578+
clang::Expr *lambda, const clang::CXXConversionDecl *conversion) {
15761579
StrCat(ConvertFreshRValue(lambda));
1580+
auto decl = AsLambdaClass(lambda->getType());
1581+
if (decl->isGenericLambda()) {
1582+
auto call_operator = AsLambdaCallOperator(conversion);
1583+
auto proto = call_operator->getType()->castAs<clang::FunctionProtoType>();
1584+
StrCat(std::format(".spec::<{}>({})", ConvertFunctionPointerType(proto),
1585+
GetLambdaSpecializationIndex(call_operator)));
1586+
}
15771587
}
15781588

15791589
void ConverterRefCount::ConvertFunctionToFunctionPointer(

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ class ConverterRefCount final : public Converter {
113113

114114
void ConvertPrintf(clang::CallExpr *expr) override;
115115

116-
void EmitFnPtrCall(clang::Expr *callee) override;
116+
void EmitFnPtrCall(clang::CallExpr *expr) override;
117117

118118
bool VisitLambdaExpr(clang::LambdaExpr *expr) override;
119119

@@ -124,7 +124,8 @@ class ConverterRefCount final : public Converter {
124124
void ConvertLambdaCapture(const clang::FieldDecl *field,
125125
clang::Expr *init) override;
126126

127-
void ConvertLambdaToFunctionPointer(clang::Expr *lambda) override;
127+
void ConvertLambdaToFunctionPointer(
128+
clang::Expr *lambda, const clang::CXXConversionDecl *conversion) override;
128129

129130
void
130131
ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl) override;

0 commit comments

Comments
 (0)