Skip to content

Commit 2a57d76

Browse files
authored
Translate moveable/copyable/destroyable lambdas (#458)
1 parent ccbecd4 commit 2a57d76

29 files changed

Lines changed: 771 additions & 80 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,14 @@ void Converter::EmitScopedDestructor(const clang::VarDecl *decl) {
605605
return;
606606
}
607607
auto type = decl->getType();
608+
if (auto lambda = AsLambdaClass(type);
609+
lambda && LambdaNeedsDestruction(lambda)) {
610+
StrCat(token::kSemiColon,
611+
std::format("let _dtor_{0} = ScopedDestructorUnsafe::new(&raw mut "
612+
"{0}, |__f| __f.destroy())",
613+
GetNamedDeclAsString(decl)));
614+
return;
615+
}
608616
if (type->isReferenceType() || type->isArrayType() ||
609617
!TypeNeedsDestruction(type)) {
610618
return;
@@ -990,7 +998,7 @@ void Converter::DefineImplicitMembers(clang::CXXRecordDecl *decl) {
990998
}
991999
if (ctor->isMoveConstructor() && !ctor->isUserProvided() &&
9921000
!ctor->doesThisDeclarationHaveABody() && !ctor->isDeleted() &&
993-
!HasDefaultedCopyConstructor(decl)) {
1001+
(decl->isLambda() || !HasDefaultedCopyConstructor(decl))) {
9941002
sema_->DefineImplicitMoveConstructor(decl->getLocation(), ctor);
9951003
}
9961004
}
@@ -3306,6 +3314,11 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {
33063314
ConvertDeclRefValue(expr, member);
33073315
return false;
33083316
}
3317+
if (auto field = clang::dyn_cast<clang::FieldDecl>(member);
3318+
field && AsLambdaCapture(field)) {
3319+
ConvertDeclRefValue(expr, member);
3320+
return false;
3321+
}
33093322
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(member);
33103323
method && IsMethodOnPtr(method) && !Mapper::Contains(ctx_, expr)) {
33113324
SetUFCSReceiver(expr->getBase(), expr->isArrow(), method);
@@ -3757,6 +3770,12 @@ bool Converter::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
37573770
}
37583771

37593772
auto *ctor = expr->getConstructor();
3773+
if (IsLambdaCopyOrMoveConstructor(ctor)) {
3774+
Convert(expr->getArg(0));
3775+
StrCat(std::format(".{}()", GetCopyOrMoveName(ctor)));
3776+
SetFreshType(expr->getType());
3777+
return false;
3778+
}
37603779
if (IsPassThroughConstructor(ctor)) {
37613780
// Take suppress before recursing into the child.
37623781
bool suppress = PushSuppressIteratorClone::take(*this);
@@ -3922,6 +3941,12 @@ bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
39223941
}
39233942
StrCat(token::kComma);
39243943
ConvertLambdaClosure(decl);
3944+
ConvertLambdaCopyAndMove(decl);
3945+
if (LambdaNeedsDestruction(decl)) {
3946+
StrCat(token::kComma, "destroy", token::kAssign);
3947+
PushBrace destroy(*this);
3948+
StrCat(DestroyMembers(decl));
3949+
}
39253950
computed_expr_type_ = ComputedExprType::FreshValue;
39263951
return false;
39273952
}
@@ -3965,6 +3990,25 @@ void Converter::ConvertLambdaClosure(const clang::CXXRecordDecl *decl) {
39653990
ConvertFunctionBody(curr_function_);
39663991
}
39673992

3993+
void Converter::ConvertLambdaCopyAndMove(clang::CXXRecordDecl *decl) {
3994+
DefineImplicitMembers(decl);
3995+
for (auto ctor : decl->ctors()) {
3996+
if (!ctor->isCopyOrMoveConstructor() || ctor->isDeleted()) {
3997+
continue;
3998+
}
3999+
StrCat(token::kComma, GetCopyOrMoveName(ctor));
4000+
PushBrace brace(*this);
4001+
PushCurrFunction push_fn(*this, ctor);
4002+
for (auto init : ctor->inits()) {
4003+
assert(init->isMemberInitializer());
4004+
auto field = init->getMember();
4005+
StrCat(GetNamedDeclAsString(field), token::kColon);
4006+
ConvertVarInit(field->getType(), init->getInit());
4007+
StrCat(token::kComma);
4008+
}
4009+
}
4010+
}
4011+
39684012
bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) {
39694013
if (auto arr_ty = clang::dyn_cast<clang::ArrayType>(
39704014
expr->getType()->getCanonicalTypeInternal().getTypePtr())) {

‎cpp2rust/converter/converter.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
481481

482482
void ConvertLambdaClosure(const clang::CXXRecordDecl *decl);
483483

484+
virtual void ConvertLambdaCopyAndMove(clang::CXXRecordDecl *decl);
485+
484486
virtual bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr);
485487
virtual bool VisitCXXScalarValueInitExpr(clang::CXXScalarValueInitExpr *expr);
486488

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,18 @@ const clang::LambdaCapture *AsLambdaCapture(const clang::FieldDecl *field) {
11741174
return decl->captures_begin() + field->getFieldIndex();
11751175
}
11761176

1177+
bool IsLambdaCopyOrMoveConstructor(const clang::CXXConstructorDecl *ctor) {
1178+
auto decl = ctor->getParent();
1179+
return ctor->isCopyOrMoveConstructor() && decl->isLambda() &&
1180+
!decl->captures().empty();
1181+
}
1182+
1183+
bool LambdaNeedsDestruction(const clang::CXXRecordDecl *decl) {
1184+
return llvm::any_of(decl->fields(), [](const clang::FieldDecl *field) {
1185+
return TypeNeedsDestruction(field->getType());
1186+
});
1187+
}
1188+
11771189
clang::Expr *AsLambdaUncapturedConstant(const clang::FunctionDecl *fn,
11781190
clang::DeclRefExpr *expr) {
11791191
if (!AsLambdaOperatorCall(fn)) {

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ const clang::CXXMethodDecl *AsLambdaOperatorCall(const clang::FunctionDecl *fn);
213213

214214
const clang::LambdaCapture *AsLambdaCapture(const clang::FieldDecl *field);
215215

216+
bool IsLambdaCopyOrMoveConstructor(const clang::CXXConstructorDecl *ctor);
217+
218+
bool LambdaNeedsDestruction(const clang::CXXRecordDecl *decl);
219+
216220
clang::Expr *AsLambdaUncapturedConstant(const clang::FunctionDecl *fn,
217221
clang::DeclRefExpr *expr);
218222

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,14 @@ void ConverterRefCount::EmitScopedDestructor(const clang::VarDecl *decl) {
913913
return;
914914
}
915915
auto type = decl->getType();
916+
if (auto lambda = AsLambdaClass(type);
917+
lambda && LambdaNeedsDestruction(lambda)) {
918+
StrCat(token::kSemiColon,
919+
std::format("let _dtor_{0} = ScopedDestructor::new(&{0}, |__p| "
920+
"__p.with(|__f| __f.destroy()))",
921+
GetNamedDeclAsString(decl)));
922+
return;
923+
}
916924
if (type->isReferenceType() || type->isArrayType() ||
917925
!TypeNeedsDestruction(type)) {
918926
return;
@@ -1564,6 +1572,11 @@ void ConverterRefCount::ConvertLambdaCapture(const clang::FieldDecl *field,
15641572
Converter::ConvertLambdaCapture(field, init);
15651573
}
15661574

1575+
void ConverterRefCount::ConvertLambdaCopyAndMove(clang::CXXRecordDecl *decl) {
1576+
PushConversionKind push(*this, ConversionKind::FullRefCount);
1577+
Converter::ConvertLambdaCopyAndMove(decl);
1578+
}
1579+
15671580
void ConverterRefCount::ConvertLambdaToFunctionPointer(clang::Expr *lambda) {
15681581
StrCat(ConvertFreshRValue(lambda));
15691582
}
@@ -1980,6 +1993,11 @@ bool ConverterRefCount::VisitMemberExpr(clang::MemberExpr *expr) {
19801993
ConvertDeclRefValue(expr, member);
19811994
return false;
19821995
}
1996+
if (auto field = clang::dyn_cast<clang::FieldDecl>(member);
1997+
field && AsLambdaCapture(field)) {
1998+
ConvertDeclRefValue(expr, member);
1999+
return false;
2000+
}
19832001
bool known = Mapper::Contains(ctx_, expr);
19842002

19852003
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(member);
@@ -2361,6 +2379,13 @@ bool ConverterRefCount::VisitCXXConstructExpr(clang::CXXConstructExpr *expr) {
23612379
}
23622380

23632381
auto *ctor = expr->getConstructor();
2382+
if (IsLambdaCopyOrMoveConstructor(ctor)) {
2383+
StrCat(ConvertRValue(expr->getArg(0)),
2384+
std::format(".{}()", GetCopyOrMoveName(ctor)));
2385+
SetFreshType(expr->getType());
2386+
return false;
2387+
}
2388+
23642389
if (IsRValueConvertingConstructor(ctor) ||
23652390
(ctor->isMoveConstructor() && !IsUserDefinedDecl(ctor->getParent()))) {
23662391
StrCat(ConvertLValue(expr->getArg(0)));
@@ -3490,6 +3515,12 @@ ConverterRefCount::DestroyMembers(const clang::CXXRecordDecl *decl) {
34903515
std::string out;
34913516
for (auto *field : std::ranges::reverse_view(fields)) {
34923517
auto name = GetNamedDeclAsString(field);
3518+
if (decl->isLambda()) {
3519+
assert(!field->getType()->isArrayType());
3520+
out +=
3521+
std::format("self.{0}.as_pointer().{1}();\n", name, kDestructorName);
3522+
continue;
3523+
}
34933524
if (field->getType()->isArrayType()) {
34943525
auto *elem =
34953526
field->getType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ class ConverterRefCount final : public Converter {
124124
void ConvertLambdaCapture(const clang::FieldDecl *field,
125125
clang::Expr *init) override;
126126

127+
void ConvertLambdaCopyAndMove(clang::CXXRecordDecl *decl) override;
128+
127129
void ConvertLambdaToFunctionPointer(clang::Expr *lambda) override;
128130

129131
void

‎libcc2rs-macros/src/lambda.rs‎

Lines changed: 89 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,19 @@ use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree};
66
use quote::quote;
77
use std::collections::HashSet;
88
use syn::parse::{Parse, ParseStream};
9-
use syn::{Block, Error, ExprClosure, Pat, Result, Stmt, Token, parse_macro_input};
9+
use syn::punctuated::Punctuated;
10+
use syn::{
11+
Block, Error, ExprClosure, FieldValue, Pat, Result, Stmt, Token, braced, parse_macro_input,
12+
};
13+
14+
type CaptureInits = Punctuated<FieldValue, Token![,]>;
1015

1116
struct Lambda {
1217
captures: Block,
1318
closure: ExprClosure,
19+
copy_from: Option<CaptureInits>,
20+
move_from: Option<CaptureInits>,
21+
destroy: Option<proc_macro2::TokenStream>,
1422
}
1523

1624
impl Parse for Lambda {
@@ -19,7 +27,37 @@ impl Parse for Lambda {
1927
input.parse::<Token![,]>()?;
2028
let closure = input.parse()?;
2129
input.parse::<Option<Token![,]>>()?;
22-
Ok(Lambda { captures, closure })
30+
let mut copy_from = None;
31+
let mut move_from = None;
32+
let mut destroy = None;
33+
while !input.is_empty() {
34+
let name: Ident = input.parse()?;
35+
if name == "destroy" {
36+
input.parse::<Token![=]>()?;
37+
}
38+
let body;
39+
braced!(body in input);
40+
if name == "copy_from" {
41+
copy_from = Some(body.parse_terminated(FieldValue::parse, Token![,])?);
42+
} else if name == "move_from" {
43+
move_from = Some(body.parse_terminated(FieldValue::parse, Token![,])?);
44+
} else if name == "destroy" {
45+
destroy = Some(body.parse()?);
46+
} else {
47+
return Err(Error::new(
48+
name.span(),
49+
"expected `copy_from`, `move_from` or `destroy`",
50+
));
51+
}
52+
input.parse::<Option<Token![,]>>()?;
53+
}
54+
Ok(Lambda {
55+
captures,
56+
closure,
57+
copy_from,
58+
move_from,
59+
destroy,
60+
})
2361
}
2462
}
2563

@@ -115,6 +153,29 @@ fn rewrite_captures(
115153
Ok(out.into_iter().collect())
116154
}
117155

156+
fn expand_constructor(
157+
inits: &Option<CaptureInits>,
158+
names: &HashSet<String>,
159+
is_unsafe: bool,
160+
) -> Result<proc_macro2::TokenStream> {
161+
let Some(inits) = inits else {
162+
return Ok(quote! { unreachable!("the lambda has no such constructor") });
163+
};
164+
let mut fields = Vec::new();
165+
for init in inits {
166+
let member = &init.member;
167+
let expr = &init.expr;
168+
let expr = rewrite_captures(quote! { #expr }, names, false)?;
169+
fields.push(quote! { #member: #expr });
170+
}
171+
let body = quote! { __Lambda { #(#fields,)* } };
172+
Ok(if is_unsafe {
173+
quote! { unsafe { #body } }
174+
} else {
175+
body
176+
})
177+
}
178+
118179
fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenStream> {
119180
let mut names = Vec::new();
120181
let mut types = Vec::new();
@@ -151,14 +212,23 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
151212
let body = &lambda.closure.body;
152213
let captured = names.iter().map(|name| name.to_string()).collect();
153214
let body = rewrite_captures(quote! { #body }, &captured, false)?;
154-
let (receiver, body, constructor) = if is_unsafe {
215+
let copy_from = expand_constructor(&lambda.copy_from, &captured, is_unsafe)?;
216+
let move_from = expand_constructor(&lambda.move_from, &captured, is_unsafe)?;
217+
let destroy = &lambda.destroy;
218+
let (receiver, body, destroy, constructor) = if is_unsafe {
155219
(
156220
quote! { &mut self },
157221
quote! { unsafe { #body } },
222+
quote! { unsafe { #destroy } },
158223
quote! { from_lambda_unsafe },
159224
)
160225
} else {
161-
(quote! { &self }, body, quote! { from_lambda })
226+
(
227+
quote! { &self },
228+
body,
229+
quote! { #destroy },
230+
quote! { from_lambda },
231+
)
162232
};
163233

164234
Ok(quote! {{
@@ -170,10 +240,25 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
170240
fn call(#receiver #(, #params)*) #output {
171241
#body
172242
}
243+
#[allow(unused_unsafe)]
244+
fn copy_from(#receiver) -> Self {
245+
#copy_from
246+
}
247+
#[allow(unused_unsafe)]
248+
fn move_from(#receiver) -> Self {
249+
#move_from
250+
}
251+
#[allow(unused_unsafe)]
252+
fn destroy(#receiver) {
253+
#destroy
254+
}
173255
}
174256
::libcc2rs::FnPtr::<fn(#(#param_types),*) #output>::#constructor(
175257
__Lambda { #(#names: #inits,)* },
176258
__Lambda::call,
259+
__Lambda::copy_from,
260+
__Lambda::move_from,
261+
__Lambda::destroy,
177262
)
178263
}})
179264
}

0 commit comments

Comments
 (0)