Conversation
|
Instead of going this way, can't you capture a pointer by value instead to keep using Rust closures? |
Capturing the pointer by value only fixes the borrowing problem. But it does not fix the following patterns that I need to support in following PRs:
|
Can you give concrete examples in C++ and in Rust to show why they break? |
auto f = [](auto x) { return x; };
f(1);
f(2.5);Closures in rust cannot be generic. The argument must be a fixed type. let f = |x| x;
f(1);
f(2.5);This translation fails on The auto lambda is a template lambda in disguise let f_i32 = lambda_i32 {};
let f_f32 = lambda_f32{};
f_i32(1);
f_f32(2.5);
auto cmp = [](int a, int b) { return a > b; };
std::set<int, decltype(cmp)> s;Captureless lambdas are default constructible. This is relevant for the constructor of set for example that does: set()
: set(Compare()) {}Rust closures are not defaultable.
template <typename Pred>
struct S {
Pred p; // p has type of lambda
int call() { return p(1, 2); }
};
S<decltype([](int a, int b) { return a + b; })> s;
s.call();Each field must have a concrete type. Closures don't have a spellable type: struct S_??? {
pub p: ???;
}With lambdas translated as structs the translated code becomes: #[derive(Default, Copy, Clone)]
struct S_lambda_1 {
pub p: lambda_1;
}
template <typename Cmp> int f(int a, int b, Cmp cmp) { return cmp(a, b) ? a : b; }
f(1, 2, std::less<int>{});
f(1, 2, MyCustomComparator{});
f(1, 2, [](int a, int b) { return a > b; });
To summarize, lambdas are a superset of closures so they don't map directly in the generated code. |
|
I don't see a hard reason in any of those examples. For templates, you need multiple closures, which is not different than creating multiple functions. Translating lambdas into functions is very annoying. Plus you lose the closure part; you need to add additional arguments to the function. |
This PR translates a lambda
[]() { return 42; }as:Each captured variable becomes a field of the struct: by value captures are plain values and by reference captures are pointers. Captured this becomes
this_inside the translated body. Captureless lambdas additionally have an associated method calledto_free_functionthat is used to convert between lambda and free function.Every lambda implementes the
libcc2rs::Callabletrait so that rules can call into user-defined lambdas. Ideally we would implement theFntrait forlambda_0, but that is not available in stable Rust.This fixes #314. In the new translation, the body of lambda functions is translated only once and does not create borrow conflicts.
Notes about the implementation:
Inside the body of the lambda, clang represents usages of captured variables as a DeclRefExpr that points to a VarDecl outside the lambda. Clang also creates FieldDecl's inside the lambda that decide the capture type: by value/by reference. All this information is used in VisitDeclRefExpr.