Skip to content

Commit 47522c8

Browse files
authored
Translate lambdas using FnPtr (#439)
Fixes #314.
1 parent a69e9a0 commit 47522c8

227 files changed

Lines changed: 2647 additions & 814 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎cpp2rust/converter/converter.cpp‎

Lines changed: 118 additions & 77 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));
@@ -282,10 +275,8 @@ bool Converter::VisitReferenceType(clang::ReferenceType *type) {
282275
}
283276

284277
std::string
285-
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto,
286-
FnProtoType kind) {
287-
std::string result =
288-
(kind == FnProtoType::LambdaCallOperator ? "impl Fn(" : "fn(");
278+
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto) {
279+
std::string result = "fn(";
289280
for (auto p_ty : proto->param_types()) {
290281
result += ToString(p_ty);
291282
result += ',';
@@ -527,20 +518,6 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
527518
return true;
528519
}
529520

530-
bool Converter::ConvertLambdaVarDecl(clang::VarDecl *decl) {
531-
if (decl->getType()->isFunctionPointerType()) {
532-
return false;
533-
}
534-
if (decl->hasInit()) {
535-
if (clang::isa<clang::LambdaExpr>(
536-
decl->getInit()->IgnoreUnlessSpelledInSource())) {
537-
// Lambdas are inlined at the call site.
538-
return true;
539-
}
540-
}
541-
return false;
542-
}
543-
544521
void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
545522
if (decl->hasInit()) {
546523
ConvertVarInit(decl->getType(), decl->getInit());
@@ -599,9 +576,6 @@ bool Converter::VisitVarDecl(clang::VarDecl *decl) {
599576
decl->getDeclContext()->isDependentContext()) {
600577
return false;
601578
}
602-
if (ConvertLambdaVarDecl(decl)) {
603-
return false;
604-
}
605579

606580
if (IsGlobalVar(decl)) {
607581
ConvertGlobalVarDecl(decl);
@@ -677,6 +651,11 @@ bool Converter::TypeDerivesDefault(clang::QualType qual_type) {
677651
}
678652
}
679653

654+
if (auto lambda = AsLambdaClass(qual_type);
655+
lambda && lambda->captures().empty()) {
656+
return false;
657+
}
658+
680659
return true;
681660
}
682661

@@ -1846,6 +1825,14 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
18461825
return false;
18471826
}
18481827

1828+
if (auto conversion = clang::dyn_cast_or_null<clang::CXXConversionDecl>(
1829+
expr->getCalleeDecl());
1830+
conversion && conversion->getParent()->isLambda()) {
1831+
ConvertLambdaToFunctionPointer(clang::cast<clang::CXXMemberCallExpr>(expr)
1832+
->getImplicitObjectArgument());
1833+
return false;
1834+
}
1835+
18491836
if (IsImplicitAssignmentCall(expr) &&
18501837
!Mapper::Contains(ctx_, expr->getCallee())) {
18511838
auto *call = clang::cast<clang::CXXMemberCallExpr>(expr);
@@ -1923,7 +1910,18 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
19231910
return false;
19241911
}
19251912

1913+
void Converter::ConvertLambdaToFunctionPointer(clang::Expr *lambda) {
1914+
StrCat("Some");
1915+
PushParen paren(*this);
1916+
ConvertLambdaClosure(AsLambdaClass(lambda->getType()));
1917+
computed_expr_type_ = ComputedExprType::FreshPointer;
1918+
}
1919+
19261920
void Converter::EmitFnPtrCall(clang::Expr *callee) {
1921+
if (AsLambdaClass(callee->getType())) {
1922+
StrCat(ConvertRValue(callee), ".call");
1923+
return;
1924+
}
19271925
{
19281926
PushParen paren(*this);
19291927
Convert(callee);
@@ -1992,7 +1990,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
19921990
unsigned num_named_params =
19931991
function ? function->getNumParams() : proto->getNumParams();
19941992
info.is_variadic = function ? function->isVariadic() : proto->isVariadic();
1995-
info.is_fn_ptr_call = !function;
1993+
info.is_fn_ptr_call = !function || AsLambdaClass(callee->getType());
19961994
info.is_libc_passthrough =
19971995
RuleRegistry::IsLibcPassthrough(ctx_, GetCalleeOrExpr(expr));
19981996

@@ -3080,16 +3078,21 @@ std::string Converter::ConvertDeclRef(clang::Expr *expr,
30803078
}
30813079

30823080
bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
3081+
if (auto init = AsLambdaUncapturedConstant(curr_function_, expr)) {
3082+
Convert(init);
3083+
return false;
3084+
}
30833085
ConvertDeclRefValue(expr, expr->getDecl());
30843086
return false;
30853087
}
30863088

30873089
void Converter::ConvertDeclRefValue(clang::Expr *expr, clang::ValueDecl *decl) {
30883090
auto str = ConvertDeclRef(expr, decl);
3091+
auto type = GetDeclRefType(curr_function_, expr, decl);
30893092

3090-
if (decl->getType()->getAs<clang::ReferenceType>() && !isAddrOf() &&
3093+
if (type->getAs<clang::ReferenceType>() && !isAddrOf() &&
30913094
!map_iter_decls_.contains(clang::dyn_cast<clang::VarDecl>(decl))) {
3092-
EmitDeref(std::move(str), decl->getType().getNonReferenceType());
3095+
EmitDeref(std::move(str), type.getNonReferenceType());
30933096
SetValueFreshness(expr->getType());
30943097
return;
30953098
}
@@ -3104,23 +3107,8 @@ void Converter::ConvertDeclRefValue(clang::Expr *expr, clang::ValueDecl *decl) {
31043107
return;
31053108
}
31063109

3107-
if (auto var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
3108-
if (!var_decl->getType()->isFunctionPointerType()) {
3109-
if (auto init = var_decl->getInit()) {
3110-
if (auto lambda = clang::dyn_cast<clang::LambdaExpr>(
3111-
init->IgnoreUnlessSpelledInSource())) {
3112-
PushParen paren(*this);
3113-
VisitLambdaExpr(lambda);
3114-
computed_expr_type_ = ComputedExprType::FreshValue;
3115-
return;
3116-
}
3117-
}
3118-
}
3119-
}
3120-
3121-
if (!decl->getType()->getAs<clang::ReferenceType>() && isAddrOf()) {
3122-
StrCat(token::kRef, decl->getType().isConstQualified() ? "" : keyword_mut_,
3123-
str);
3110+
if (!type->getAs<clang::ReferenceType>() && isAddrOf()) {
3111+
StrCat(token::kRef, type.isConstQualified() ? "" : keyword_mut_, str);
31243112
computed_expr_type_ = ComputedExprType::FreshPointer;
31253113
return;
31263114
}
@@ -3272,7 +3260,8 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
32723260

32733261
void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
32743262
const clang::CXXMethodDecl *method) {
3275-
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts())) {
3263+
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts()) &&
3264+
!AsLambdaOperatorCall(curr_function_)) {
32763265
bool in_ctor =
32773266
curr_function_ && clang::isa<clang::CXXConstructorDecl>(curr_function_);
32783267
ufcs_receiver_ = in_ctor ? "&mut this" : keyword::kSelfValue;
@@ -3356,8 +3345,8 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
33563345
}
33573346

33583347
auto *base = expr->getBase();
3359-
bool base_is_this =
3360-
clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) && !ThisIsRustPtr();
3348+
bool base_is_this = clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) &&
3349+
!ThisIsRustPtr() && !AsLambdaOperatorCall(curr_function_);
33613350
PushExprKind push(*this, isLValue() ? ExprKind::LValue : ExprKind::RValue);
33623351
if (base_is_this) {
33633352
StrCat(clang::isa<clang::CXXConstructorDecl>(curr_function_)
@@ -3382,6 +3371,14 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
33823371
}
33833372

33843373
bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
3374+
if (auto field = AsLambdaCaptureThis(curr_function_)) {
3375+
auto name = GetNamedDeclAsString(field);
3376+
StrCat(field->getType()->isPointerType()
3377+
? name
3378+
: std::format("(&raw mut {})", name));
3379+
computed_expr_type_ = ComputedExprType::FreshPointer;
3380+
return false;
3381+
}
33853382
if (clang::isa<clang::CXXConstructorDecl>(curr_function_)) {
33863383
StrCat("&raw mut this");
33873384
} else {
@@ -3837,22 +3834,63 @@ bool Converter::VisitConstantExpr(clang::ConstantExpr *expr) {
38373834
}
38383835

38393836
bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
3840-
if (isAddrOf() && expr->capture_size() == 0) {
3841-
StrCat("Some");
3837+
auto decl = expr->getLambdaClass();
3838+
if (decl->captures().empty()) {
3839+
ConvertCapturelessLambda(decl);
3840+
return false;
3841+
}
3842+
StrCat(LambdaMacro());
3843+
PushParen paren(*this);
3844+
{
3845+
PushBrace captures(*this);
3846+
auto init = expr->capture_init_begin();
3847+
for (auto field : decl->fields()) {
3848+
ConvertLambdaCapture(field, *init++);
3849+
}
38423850
}
3851+
StrCat(token::kComma);
3852+
ConvertLambdaClosure(decl);
3853+
computed_expr_type_ = ComputedExprType::FreshValue;
3854+
return false;
3855+
}
3856+
3857+
void Converter::ConvertCapturelessLambda(const clang::CXXRecordDecl *decl) {
3858+
auto proto = decl->getLambdaCallOperator()
3859+
->getType()
3860+
->castAs<clang::FunctionProtoType>();
3861+
StrCat(std::format("FnPtr::<{}>::new", ConvertFunctionPointerType(proto)));
38433862
PushParen paren(*this);
3863+
ConvertLambdaClosure(decl);
3864+
computed_expr_type_ = ComputedExprType::FreshValue;
3865+
}
3866+
3867+
void Converter::ConvertLambdaCapture(const clang::FieldDecl *field,
3868+
clang::Expr *init) {
3869+
StrCat(keyword::kLet, GetNamedDeclAsString(field), token::kColon);
3870+
Convert(field->getType());
3871+
StrCat(token::kAssign);
3872+
ConvertVarInit(field->getType(), init);
3873+
StrCat(token::kSemiColon);
3874+
}
3875+
3876+
void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl) {
3877+
auto call_operator = decl->getLambdaCallOperator();
38443878
StrCat('|');
3845-
for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) {
3879+
for (auto p : call_operator->parameters()) {
38463880
StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()),
38473881
token::kComma);
38483882
}
3849-
StrCat("| {");
3850-
EmitFunctionPreamble(expr->getLambdaClass()->getLambdaCallOperator());
3851-
PushCurrFunction push_fn(*this,
3852-
expr->getLambdaClass()->getLambdaCallOperator());
3883+
StrCat('|');
3884+
ConvertFunctionReturnType(call_operator);
3885+
PushBrace body(*this);
3886+
std::optional<PushBrace> unsafe_brace;
3887+
if (decl->captures().empty()) {
3888+
StrCat(keyword_unsafe_);
3889+
unsafe_brace.emplace(*this);
3890+
}
3891+
EmitFunctionPreamble(call_operator);
3892+
PushCurrFunction push_fn(*this, call_operator);
38533893
ConvertFunctionBody(curr_function_);
3854-
StrCat('}');
3855-
return false;
38563894
}
38573895

38583896
bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) {
@@ -4054,6 +4092,17 @@ std::string Converter::GetArrayDefaultAsString(clang::QualType qual_type) {
40544092
}
40554093

40564094
std::string Converter::GetDefaultAsString(clang::QualType qual_type) {
4095+
if (auto lambda = AsLambdaClass(qual_type);
4096+
lambda && lambda->captures().empty()) {
4097+
if (HasStaticLocal(lambda->getLambdaCallOperator()->getBody())) {
4098+
llvm::report_fatal_error("default-constructed lambda with a static local "
4099+
"is not supported yet");
4100+
}
4101+
Buffer buf(*this);
4102+
ConvertCapturelessLambda(lambda);
4103+
return std::move(buf).str();
4104+
}
4105+
40574106
if (IsVaListType(qual_type)) {
40584107
computed_expr_type_ = ComputedExprType::FreshValue;
40594108
return "VaList::default()";
@@ -4161,7 +4210,7 @@ std::string Converter::GetRecordName(const clang::NamedDecl *decl) const {
41614210
std::vector<const char *>
41624211
Converter::GetStructAttributes(const clang::RecordDecl *decl) {
41634212
if (decl->isUnion()) {
4164-
return {"Copy", "Clone"};
4213+
return {"Copy", "Clone", "FnPtrArg"};
41654214
}
41664215

41674216
std::vector<const char *> struct_attrs;
@@ -4176,6 +4225,7 @@ Converter::GetStructAttributes(const clang::RecordDecl *decl) {
41764225

41774226
if (RecordImplementsClone(decl)) {
41784227
struct_attrs.emplace_back("VaArg");
4228+
struct_attrs.emplace_back("FnPtrArg");
41794229
}
41804230

41814231
if (RecordDerivesDefault(decl)) {
@@ -4215,15 +4265,6 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
42154265
StrCat(keyword_mut_);
42164266
}
42174267
}
4218-
if (qual_type->isFunctionPointerType()) {
4219-
if (auto *lambda = clang::dyn_cast<clang::LambdaExpr>(
4220-
expr->IgnoreUnlessSpelledInSource())) {
4221-
PushExprKind push(*this, ExprKind::AddrOf);
4222-
PushInitType init_type(*this, qual_type);
4223-
VisitLambdaExpr(lambda);
4224-
return;
4225-
}
4226-
}
42274268
auto *ignore_casts = expr->IgnoreCasts();
42284269
// FIXME: this looks very complicated
42294270
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);
@@ -334,6 +329,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
334329

335330
virtual void EmitFnPtrCall(clang::Expr *callee);
336331

332+
virtual void ConvertLambdaToFunctionPointer(clang::Expr *lambda);
333+
337334
virtual void
338335
ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl);
339336

@@ -465,6 +462,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
465462

466463
virtual bool VisitLambdaExpr(clang::LambdaExpr *expr);
467464

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

0 commit comments

Comments
 (0)