Skip to content

Commit 7ca511e

Browse files
committed
Don't emit self in the lambda body
1 parent 7897e0b commit 7ca511e

5 files changed

Lines changed: 107 additions & 17 deletions

File tree

‎cpp2rust/converter/converter.cpp‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2972,11 +2972,6 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
29722972

29732973
std::string Converter::ConvertDeclRef(clang::Expr *expr,
29742974
clang::ValueDecl *decl) {
2975-
if (auto field = AsLambdaCaptureField(curr_function_, expr)) {
2976-
return std::format("{}.{}", keyword::kSelfValue,
2977-
GetNamedDeclAsString(field));
2978-
}
2979-
29802975
if (isAddrOf()) {
29812976
clang::Expr *addrof_op = ToAddrOf(ctx_, expr);
29822977
if (auto str = GetMappedAsString(addrof_op); !str.empty()) {
@@ -3316,8 +3311,8 @@ bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
33163311
if (auto field = AsLambdaCaptureThis(curr_function_)) {
33173312
auto name = GetNamedDeclAsString(field);
33183313
StrCat(field->getType()->isPointerType()
3319-
? std::format("{}.{}", keyword::kSelfValue, name)
3320-
: std::format("(&raw mut {}.{})", keyword::kSelfValue, name));
3314+
? name
3315+
: std::format("(&raw mut {})", name));
33213316
computed_expr_type_ = ComputedExprType::FreshPointer;
33223317
return false;
33233318
}

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,8 +1082,8 @@ clang::Expr *AsLambdaUncapturedConstant(const clang::FunctionDecl *fn,
10821082
return var->getInit();
10831083
}
10841084

1085-
const clang::FieldDecl *AsLambdaCaptureField(const clang::FunctionDecl *fn,
1086-
const clang::Expr *expr) {
1085+
static const clang::FieldDecl *
1086+
AsLambdaCaptureField(const clang::FunctionDecl *fn, const clang::Expr *expr) {
10871087
auto call = AsLambdaOperatorCall(fn);
10881088
auto ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
10891089
if (!call || !ref || !ref->refersToEnclosingVariableOrCapture()) {

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,6 @@ const clang::LambdaCapture *AsLambdaCapture(const clang::FieldDecl *field);
201201
clang::Expr *AsLambdaUncapturedConstant(const clang::FunctionDecl *fn,
202202
clang::DeclRefExpr *expr);
203203

204-
const clang::FieldDecl *AsLambdaCaptureField(const clang::FunctionDecl *fn,
205-
const clang::Expr *expr);
206-
207204
const clang::FieldDecl *AsLambdaCaptureThis(const clang::FunctionDecl *fn);
208205

209206
clang::QualType GetDeclRefType(const clang::FunctionDecl *fn,

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3267,9 +3267,8 @@ bool ConverterRefCount::VisitCXXThisExpr(
32673267
if (auto field = AsLambdaCaptureThis(curr_function_)) {
32683268
auto name = GetNamedDeclAsString(field);
32693269
StrCat(field->getType()->isPointerType()
3270-
? std::format("(*{}.{}.borrow()).clone()", keyword::kSelfValue,
3271-
name)
3272-
: std::format("{}.{}.as_pointer()", keyword::kSelfValue, name));
3270+
? std::format("(*{}.borrow()).clone()", name)
3271+
: std::format("{}.as_pointer()", name));
32733272
computed_expr_type_ = ComputedExprType::FreshPointer;
32743273
return false;
32753274
}

‎libcc2rs-macros/src/lambda.rs‎

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
// Distributed under the MIT license that can be found in the LICENSE file.
33

44
use proc_macro::TokenStream;
5+
use proc_macro2::{Group, Ident, Punct, Spacing, TokenTree};
56
use quote::quote;
7+
use std::collections::HashSet;
68
use syn::parse::{Parse, ParseStream};
79
use syn::{Block, Error, ExprClosure, Pat, Result, Stmt, Token, parse_macro_input};
810

@@ -21,6 +23,98 @@ impl Parse for Lambda {
2123
}
2224
}
2325

26+
fn is_punct(token: Option<&TokenTree>, ch: char) -> bool {
27+
matches!(token, Some(TokenTree::Punct(punct)) if punct.as_char() == ch)
28+
}
29+
30+
fn is_ident(token: Option<&TokenTree>, name: &str) -> bool {
31+
matches!(token, Some(TokenTree::Ident(ident)) if ident == name)
32+
}
33+
34+
fn is_lambda_macro(name: Option<&TokenTree>, bang: Option<&TokenTree>) -> bool {
35+
is_punct(bang, '!') && (is_ident(name, "lambda") || is_ident(name, "lambda_unsafe"))
36+
}
37+
38+
fn rewrite_nested_lambda(
39+
tokens: proc_macro2::TokenStream,
40+
names: &HashSet<String>,
41+
) -> Result<proc_macro2::TokenStream> {
42+
let mut tokens = tokens.into_iter();
43+
let mut out = Vec::new();
44+
if let Some(TokenTree::Group(captures)) = tokens.next() {
45+
let mut group = Group::new(
46+
captures.delimiter(),
47+
rewrite_captures(captures.stream(), names, true)?,
48+
);
49+
group.set_span(captures.span());
50+
out.push(TokenTree::Group(group));
51+
}
52+
out.extend(tokens);
53+
Ok(out.into_iter().collect())
54+
}
55+
56+
fn rewrite_captures(
57+
tokens: proc_macro2::TokenStream,
58+
names: &HashSet<String>,
59+
in_capture_block: bool,
60+
) -> Result<proc_macro2::TokenStream> {
61+
let tokens: Vec<TokenTree> = tokens.into_iter().collect();
62+
let mut out = Vec::new();
63+
let mut in_params = false;
64+
for (i, token) in tokens.iter().enumerate() {
65+
let prev = i.checked_sub(1).map(|j| &tokens[j]);
66+
let prev2 = i.checked_sub(2).map(|j| &tokens[j]);
67+
let next = tokens.get(i + 1);
68+
let next2 = tokens.get(i + 2);
69+
match token {
70+
TokenTree::Group(group) => {
71+
let stream = if is_lambda_macro(prev2, prev) {
72+
rewrite_nested_lambda(group.stream(), names)?
73+
} else {
74+
rewrite_captures(group.stream(), names, in_capture_block)?
75+
};
76+
let mut rewritten = Group::new(group.delimiter(), stream);
77+
rewritten.set_span(group.span());
78+
out.push(TokenTree::Group(rewritten));
79+
}
80+
TokenTree::Punct(punct) if punct.as_char() == '|' => {
81+
if in_params {
82+
in_params = false;
83+
} else if is_punct(next, '|')
84+
|| (matches!(next, Some(TokenTree::Ident(_))) && is_punct(next2, ':'))
85+
{
86+
in_params = true;
87+
}
88+
out.push(token.clone());
89+
}
90+
TokenTree::Ident(ident) if names.contains(&ident.to_string()) => {
91+
let before_colon = is_punct(next, ':') && !is_punct(next2, ':');
92+
let is_let =
93+
is_ident(prev, "let") || (is_ident(prev, "mut") && is_ident(prev2, "let"));
94+
if !in_capture_block && (is_let || (in_params && before_colon)) {
95+
return Err(Error::new(
96+
ident.span(),
97+
format!("`{ident}` shadows a capture of the lambda"),
98+
));
99+
}
100+
let is_member = is_punct(prev, '.') && !is_punct(prev2, '.');
101+
let is_path = is_punct(prev, ':') && is_punct(prev2, ':');
102+
let is_label = is_punct(prev, '\'');
103+
let is_macro = is_punct(next, '!') && !is_punct(next2, '=');
104+
if is_member || is_path || is_label || is_macro || before_colon {
105+
out.push(token.clone());
106+
} else {
107+
out.push(TokenTree::Ident(Ident::new("self", ident.span())));
108+
out.push(TokenTree::Punct(Punct::new('.', Spacing::Alone)));
109+
out.push(token.clone());
110+
}
111+
}
112+
_ => out.push(token.clone()),
113+
}
114+
}
115+
Ok(out.into_iter().collect())
116+
}
117+
24118
fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenStream> {
25119
let mut names = Vec::new();
26120
let mut types = Vec::new();
@@ -35,7 +129,10 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
35129
"expected `let <name>: <type> = <init>;` for a capture",
36130
));
37131
};
38-
names.push(&pat.pat);
132+
let Pat::Ident(name) = &*pat.pat else {
133+
return Err(Error::new_spanned(pat, "expected a name for a capture"));
134+
};
135+
names.push(&name.ident);
39136
types.push(&pat.ty);
40137
inits.push(&init.expr);
41138
}
@@ -52,14 +149,16 @@ fn expand_lambda(lambda: Lambda, is_unsafe: bool) -> Result<proc_macro2::TokenSt
52149

53150
let output = &lambda.closure.output;
54151
let body = &lambda.closure.body;
152+
let captured = names.iter().map(|name| name.to_string()).collect();
153+
let body = rewrite_captures(quote! { #body }, &captured, false)?;
55154
let (receiver, body, constructor) = if is_unsafe {
56155
(
57156
quote! { &mut self },
58157
quote! { unsafe { #body } },
59158
quote! { from_lambda_unsafe },
60159
)
61160
} else {
62-
(quote! { &self }, quote! { #body }, quote! { from_lambda })
161+
(quote! { &self }, body, quote! { from_lambda })
63162
};
64163

65164
Ok(quote! {{

0 commit comments

Comments
 (0)