Skip to content

Commit 4ea6445

Browse files
committed
Translate lambdas using FnPtr and lambada macro
1 parent 7f71a97 commit 4ea6445

6 files changed

Lines changed: 316 additions & 102 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 121 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ bool Converter::Convert(clang::QualType qual_type) {
117117
}
118118

119119
if (auto decl = qual_type->getAsRecordDecl();
120-
decl && IsUserDefinedDecl(decl)) {
120+
decl && IsUserDefinedDecl(decl) && !AsLambdaClass(qual_type)) {
121121
record_decls_.MarkReferenced(GetRecordName(decl));
122122
}
123123

@@ -165,19 +165,12 @@ bool Converter::VisitBuiltinType(clang::BuiltinType *type) {
165165

166166
bool Converter::VisitRecordType(clang::RecordType *type) {
167167
auto *decl = type->getDecl();
168-
if (auto lambda = clang::dyn_cast<clang::CXXRecordDecl>(decl)) {
169-
if (lambda->isLambda()) {
170-
if (in_function_formals_) {
171-
StrCat(
172-
ConvertFunctionPointerType(lambda->getLambdaCallOperator()
173-
->getType()
174-
->getAs<clang::FunctionProtoType>(),
175-
FnProtoType::LambdaCallOperator));
176-
} else {
177-
StrCat('_');
178-
}
179-
return false;
180-
}
168+
if (auto lambda = AsLambdaClass(clang::QualType(type, 0))) {
169+
auto proto = lambda->getLambdaCallOperator()
170+
->getType()
171+
->castAs<clang::FunctionProtoType>();
172+
StrCat(std::format("FnPtr<{}>", ConvertFunctionPointerType(proto)));
173+
return false;
181174
}
182175

183176
StrCat(GetRecordName(decl));
@@ -278,10 +271,8 @@ bool Converter::VisitReferenceType(clang::ReferenceType *type) {
278271
}
279272

280273
std::string
281-
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto,
282-
FnProtoType kind) {
283-
std::string result =
284-
(kind == FnProtoType::LambdaCallOperator ? "impl Fn(" : "fn(");
274+
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto) {
275+
std::string result = "fn(";
285276
for (auto p_ty : proto->param_types()) {
286277
result += ToString(p_ty);
287278
result += ',';
@@ -523,20 +514,6 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
523514
return true;
524515
}
525516

526-
bool Converter::ConvertLambdaVarDecl(clang::VarDecl *decl) {
527-
if (decl->getType()->isFunctionPointerType()) {
528-
return false;
529-
}
530-
if (decl->hasInit()) {
531-
if (clang::isa<clang::LambdaExpr>(
532-
decl->getInit()->IgnoreUnlessSpelledInSource())) {
533-
// Lambdas are inlined at the call site.
534-
return true;
535-
}
536-
}
537-
return false;
538-
}
539-
540517
void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
541518
if (decl->hasInit()) {
542519
ConvertVarInit(decl->getType(), decl->getInit());
@@ -594,9 +571,6 @@ bool Converter::VisitVarDecl(clang::VarDecl *decl) {
594571
if (clang::isa<clang::VarTemplatePartialSpecializationDecl>(decl)) {
595572
return false;
596573
}
597-
if (ConvertLambdaVarDecl(decl)) {
598-
return false;
599-
}
600574

601575
if (IsGlobalVar(decl)) {
602576
ConvertGlobalVarDecl(decl);
@@ -651,6 +625,11 @@ bool Converter::RecordDerivesDefault(const clang::RecordDecl *decl) {
651625
}
652626
}
653627

628+
if (auto lambda = AsLambdaClass(f->getType());
629+
lambda && lambda->captures().empty()) {
630+
return false;
631+
}
632+
654633
// Records that contain std::array do not derive Default
655634
if (Printer::ToString(ctx_, f->getType()).contains("std::array")) {
656635
return false;
@@ -1785,6 +1764,14 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
17851764
return false;
17861765
}
17871766

1767+
if (auto conversion = clang::dyn_cast_or_null<clang::CXXConversionDecl>(
1768+
expr->getCalleeDecl());
1769+
conversion && conversion->getParent()->isLambda()) {
1770+
ConvertLambdaToFunctionPointer(clang::cast<clang::CXXMemberCallExpr>(expr)
1771+
->getImplicitObjectArgument());
1772+
return false;
1773+
}
1774+
17881775
if (IsImplicitAssignmentCall(expr) &&
17891776
!Mapper::Contains(ctx_, expr->getCallee())) {
17901777
auto *call = clang::cast<clang::CXXMemberCallExpr>(expr);
@@ -1862,7 +1849,18 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
18621849
return false;
18631850
}
18641851

1852+
void Converter::ConvertLambdaToFunctionPointer(clang::Expr *lambda) {
1853+
StrCat("Some");
1854+
PushParen paren(*this);
1855+
ConvertLambdaClosure(AsLambdaClass(lambda->getType()));
1856+
computed_expr_type_ = ComputedExprType::FreshPointer;
1857+
}
1858+
18651859
void Converter::EmitFnPtrCall(clang::Expr *callee) {
1860+
if (AsLambdaClass(callee->getType())) {
1861+
StrCat(ConvertRValue(callee), ".call");
1862+
return;
1863+
}
18661864
{
18671865
PushParen paren(*this);
18681866
Convert(callee);
@@ -1931,7 +1929,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
19311929
unsigned num_named_params =
19321930
function ? function->getNumParams() : proto->getNumParams();
19331931
info.is_variadic = function ? function->isVariadic() : proto->isVariadic();
1934-
info.is_fn_ptr_call = !function;
1932+
info.is_fn_ptr_call = !function || AsLambdaClass(callee->getType());
19351933
info.is_libc_passthrough =
19361934
RuleRegistry::IsLibcPassthrough(ctx_, GetCalleeOrExpr(expr));
19371935

@@ -2974,6 +2972,11 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
29742972

29752973
std::string Converter::ConvertDeclRef(clang::Expr *expr,
29762974
clang::ValueDecl *decl) {
2975+
if (auto field = AsLambdaCaptureField(curr_function_, expr)) {
2976+
return std::format("{}.{}", keyword::kSelfValue,
2977+
GetNamedDeclAsString(field));
2978+
}
2979+
29772980
if (isAddrOf()) {
29782981
clang::Expr *addrof_op = ToAddrOf(ctx_, expr);
29792982
if (auto str = GetMappedAsString(addrof_op); !str.empty()) {
@@ -3017,16 +3020,21 @@ std::string Converter::ConvertDeclRef(clang::Expr *expr,
30173020
}
30183021

30193022
bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
3023+
if (auto init = AsLambdaUncapturedConstant(curr_function_, expr)) {
3024+
Convert(init);
3025+
return false;
3026+
}
30203027
ConvertDeclRefValue(expr, expr->getDecl());
30213028
return false;
30223029
}
30233030

30243031
void Converter::ConvertDeclRefValue(clang::Expr *expr, clang::ValueDecl *decl) {
30253032
auto str = ConvertDeclRef(expr, decl);
3033+
auto type = GetDeclRefType(curr_function_, expr, decl);
30263034

3027-
if (decl->getType()->getAs<clang::ReferenceType>() && !isAddrOf() &&
3035+
if (type->getAs<clang::ReferenceType>() && !isAddrOf() &&
30283036
!map_iter_decls_.contains(clang::dyn_cast<clang::VarDecl>(decl))) {
3029-
EmitDeref(std::move(str), decl->getType().getNonReferenceType());
3037+
EmitDeref(std::move(str), type.getNonReferenceType());
30303038
SetValueFreshness(expr->getType());
30313039
return;
30323040
}
@@ -3041,23 +3049,8 @@ void Converter::ConvertDeclRefValue(clang::Expr *expr, clang::ValueDecl *decl) {
30413049
return;
30423050
}
30433051

3044-
if (auto var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
3045-
if (!var_decl->getType()->isFunctionPointerType()) {
3046-
if (auto init = var_decl->getInit()) {
3047-
if (auto lambda = clang::dyn_cast<clang::LambdaExpr>(
3048-
init->IgnoreUnlessSpelledInSource())) {
3049-
PushParen paren(*this);
3050-
VisitLambdaExpr(lambda);
3051-
computed_expr_type_ = ComputedExprType::FreshValue;
3052-
return;
3053-
}
3054-
}
3055-
}
3056-
}
3057-
3058-
if (!decl->getType()->getAs<clang::ReferenceType>() && isAddrOf()) {
3059-
StrCat(token::kRef, decl->getType().isConstQualified() ? "" : keyword_mut_,
3060-
str);
3052+
if (!type->getAs<clang::ReferenceType>() && isAddrOf()) {
3053+
StrCat(token::kRef, type.isConstQualified() ? "" : keyword_mut_, str);
30613054
computed_expr_type_ = ComputedExprType::FreshPointer;
30623055
return;
30633056
}
@@ -3209,7 +3202,8 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
32093202

32103203
void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
32113204
const clang::CXXMethodDecl *method) {
3212-
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts())) {
3205+
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts()) &&
3206+
!AsLambdaOperatorCall(curr_function_)) {
32133207
bool in_ctor =
32143208
curr_function_ && clang::isa<clang::CXXConstructorDecl>(curr_function_);
32153209
ufcs_receiver_ = in_ctor ? "&mut this" : keyword::kSelfValue;
@@ -3293,8 +3287,8 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
32933287
}
32943288

32953289
auto *base = expr->getBase();
3296-
bool base_is_this =
3297-
clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) && !ThisIsRustPtr();
3290+
bool base_is_this = clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) &&
3291+
!ThisIsRustPtr() && !AsLambdaOperatorCall(curr_function_);
32983292
PushExprKind push(*this, isLValue() ? ExprKind::LValue : ExprKind::RValue);
32993293
if (base_is_this) {
33003294
StrCat(clang::isa<clang::CXXConstructorDecl>(curr_function_)
@@ -3319,6 +3313,14 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
33193313
}
33203314

33213315
bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
3316+
if (auto field = AsLambdaCaptureThis(curr_function_)) {
3317+
auto name = GetNamedDeclAsString(field);
3318+
StrCat(field->getType()->isPointerType()
3319+
? std::format("{}.{}", keyword::kSelfValue, name)
3320+
: std::format("(&raw mut {}.{})", keyword::kSelfValue, name));
3321+
computed_expr_type_ = ComputedExprType::FreshPointer;
3322+
return false;
3323+
}
33223324
if (clang::isa<clang::CXXConstructorDecl>(curr_function_)) {
33233325
StrCat("&raw mut this");
33243326
} else {
@@ -3774,22 +3776,63 @@ bool Converter::VisitConstantExpr(clang::ConstantExpr *expr) {
37743776
}
37753777

37763778
bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
3777-
if (isAddrOf() && expr->capture_size() == 0) {
3778-
StrCat("Some");
3779+
auto decl = expr->getLambdaClass();
3780+
if (decl->captures().empty()) {
3781+
ConvertCapturelessLambda(decl);
3782+
return false;
37793783
}
3784+
StrCat(LambdaMacro());
37803785
PushParen paren(*this);
3786+
{
3787+
PushBrace captures(*this);
3788+
auto init = expr->capture_init_begin();
3789+
for (auto field : decl->fields()) {
3790+
ConvertLambdaCapture(field, *init++);
3791+
}
3792+
}
3793+
StrCat(token::kComma);
3794+
ConvertLambdaClosure(decl);
3795+
computed_expr_type_ = ComputedExprType::FreshValue;
3796+
return false;
3797+
}
3798+
3799+
void Converter::ConvertCapturelessLambda(const clang::CXXRecordDecl *decl) {
3800+
auto proto = decl->getLambdaCallOperator()
3801+
->getType()
3802+
->castAs<clang::FunctionProtoType>();
3803+
StrCat(std::format("FnPtr::<{}>::new", ConvertFunctionPointerType(proto)));
3804+
PushParen paren(*this);
3805+
ConvertLambdaClosure(decl);
3806+
computed_expr_type_ = ComputedExprType::FreshValue;
3807+
}
3808+
3809+
void Converter::ConvertLambdaCapture(const clang::FieldDecl *field,
3810+
clang::Expr *init) {
3811+
StrCat(keyword::kLet, GetNamedDeclAsString(field), token::kColon);
3812+
Convert(field->getType());
3813+
StrCat(token::kAssign);
3814+
ConvertVarInit(field->getType(), init);
3815+
StrCat(token::kSemiColon);
3816+
}
3817+
3818+
void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl) {
3819+
auto call_operator = decl->getLambdaCallOperator();
37813820
StrCat('|');
3782-
for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) {
3821+
for (auto p : call_operator->parameters()) {
37833822
StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()),
37843823
token::kComma);
37853824
}
3786-
StrCat("| {");
3787-
EmitFunctionPreamble(expr->getLambdaClass()->getLambdaCallOperator());
3788-
PushCurrFunction push_fn(*this,
3789-
expr->getLambdaClass()->getLambdaCallOperator());
3825+
StrCat('|');
3826+
ConvertFunctionReturnType(call_operator);
3827+
PushBrace body(*this);
3828+
std::optional<PushBrace> unsafe_brace;
3829+
if (decl->captures().empty()) {
3830+
StrCat(keyword_unsafe_);
3831+
unsafe_brace.emplace(*this);
3832+
}
3833+
EmitFunctionPreamble(call_operator);
3834+
PushCurrFunction push_fn(*this, call_operator);
37903835
ConvertFunctionBody(curr_function_);
3791-
StrCat('}');
3792-
return false;
37933836
}
37943837

37953838
bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) {
@@ -3991,6 +4034,17 @@ std::string Converter::GetArrayDefaultAsString(clang::QualType qual_type) {
39914034
}
39924035

39934036
std::string Converter::GetDefaultAsString(clang::QualType qual_type) {
4037+
if (auto lambda = AsLambdaClass(qual_type);
4038+
lambda && lambda->captures().empty()) {
4039+
if (HasStaticLocal(lambda->getLambdaCallOperator()->getBody())) {
4040+
llvm::report_fatal_error(
4041+
"default-constructed lambda with a static local is not supported");
4042+
}
4043+
Buffer buf(*this);
4044+
ConvertCapturelessLambda(lambda);
4045+
return std::move(buf).str();
4046+
}
4047+
39944048
if (IsVaListType(qual_type)) {
39954049
computed_expr_type_ = ComputedExprType::FreshValue;
39964050
return "VaList::default()";
@@ -4153,15 +4207,6 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
41534207
StrCat(keyword_mut_);
41544208
}
41554209
}
4156-
if (qual_type->isFunctionPointerType()) {
4157-
if (auto *lambda = clang::dyn_cast<clang::LambdaExpr>(
4158-
expr->IgnoreUnlessSpelledInSource())) {
4159-
PushExprKind push(*this, ExprKind::AddrOf);
4160-
PushInitType init_type(*this, qual_type);
4161-
VisitLambdaExpr(lambda);
4162-
return;
4163-
}
4164-
}
41654210
auto *ignore_casts = expr->IgnoreCasts();
41664211
// FIXME: this looks very complicated
41674212
if (auto *ctor = clang::dyn_cast<clang::CXXConstructExpr>(ignore_casts);

‎cpp2rust/converter/converter.h‎

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
6969

7070
virtual bool VisitPointerType(clang::PointerType *type);
7171

72-
enum class FnProtoType { LambdaCallOperator, FnPtr };
73-
7472
virtual std::string
75-
ConvertFunctionPointerType(const clang::FunctionProtoType *proto,
76-
FnProtoType kind = FnProtoType::FnPtr);
73+
ConvertFunctionPointerType(const clang::FunctionProtoType *proto);
7774

7875
virtual bool VisitDecayedType(clang::DecayedType *type);
7976

@@ -116,8 +113,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
116113

117114
virtual bool ConvertVarDeclSkipInit(clang::VarDecl *decl);
118115

119-
virtual bool ConvertLambdaVarDecl(clang::VarDecl *decl);
120-
121116
bool VisitRecordDecl(clang::RecordDecl *decl);
122117

123118
virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl);
@@ -332,6 +327,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
332327

333328
virtual void EmitFnPtrCall(clang::Expr *callee);
334329

330+
virtual void ConvertLambdaToFunctionPointer(clang::Expr *lambda);
331+
335332
virtual void
336333
ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl);
337334

@@ -463,6 +460,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
463460

464461
virtual bool VisitLambdaExpr(clang::LambdaExpr *expr);
465462

463+
virtual void ConvertCapturelessLambda(const clang::CXXRecordDecl *decl);
464+
465+
virtual const char *LambdaMacro() const { return "lambda_unsafe!"; }
466+
467+
virtual void ConvertLambdaCapture(const clang::FieldDecl *field,
468+
clang::Expr *init);
469+
470+
void ConvertLambdaClosure(const clang::CXXRecordDecl *decl);
471+
466472
virtual bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr);
467473
virtual bool VisitCXXScalarValueInitExpr(clang::CXXScalarValueInitExpr *expr);
468474

0 commit comments

Comments
 (0)