Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a18b058
Add Callable trait
lucic71 Sep 16, 2026
f9f5fd7
Deduplicate rules using the Callable trait
lucic71 Sep 16, 2026
3a7cbc4
Use fully qualified syntax for FnPtr::new
lucic71 Sep 16, 2026
8d5f2d1
Map types lazily
lucic71 Sep 16, 2026
6eeeeb7
Convert casted type lazily
lucic71 Sep 16, 2026
2b7394a
Pass nullable function to rules
lucic71 Sep 16, 2026
2bc199e
Update tests
lucic71 Sep 16, 2026
fd22c47
Remove nullability of arguments
lucic71 Sep 16, 2026
028e45d
Update tests
lucic71 Sep 16, 2026
5af5400
Translate lambdas as struct + operator call
lucic71 Sep 16, 2026
eec1a00
Add hoisted_records
lucic71 Sep 16, 2026
4b5bab8
Update tests
lucic71 Sep 16, 2026
dab0234
Update tests
lucic71 Sep 16, 2026
989890e
Fix naming clash between functoins and specializations
lucic71 Sep 16, 2026
2ddf78f
clang-format
lucic71 Sep 16, 2026
284ba1e
Merge branch 'function-local-records' into lambdas
lucic71 Sep 16, 2026
3bf9bdd
Merge branch 'master' into lambdas
lucic71 Sep 16, 2026
2382727
Rename to AddCallableTrait
lucic71 Sep 16, 2026
e04cad3
Check lambda in AddCloneTrait
lucic71 Sep 16, 2026
88be99f
Fix clone for lambdas
lucic71 Sep 16, 2026
b7324a5
Captureless lambdas don't have receiver
lucic71 Sep 16, 2026
70eff83
Declare lambda to function cast inline
lucic71 Sep 16, 2026
03f4c99
Always hoist lambdas
lucic71 Sep 16, 2026
73f0fa9
Synthesize an init list expr in VisitLambdaExpr
lucic71 Sep 16, 2026
d9bb6d2
Use RAII
lucic71 Sep 16, 2026
6d1e2f4
Assert on maxcallablearity
lucic71 Sep 16, 2026
0f56357
Captured variables are declrefexpr pointing to vardecl
lucic71 Sep 16, 2026
0e0ef10
Implement operator_call on Self instead of Ptr<Self>
lucic71 Sep 16, 2026
d6a2d09
Inline functions
lucic71 Sep 16, 2026
8777d62
Rename
lucic71 Sep 16, 2026
e66082e
Add to_free_function operator
lucic71 Sep 16, 2026
813cc6f
Move lambda functions in _lib
lucic71 Sep 17, 2026
7ec42f5
Remvove duplicated function
lucic71 Sep 17, 2026
1d53d95
Inline conversion of captured this
lucic71 Sep 17, 2026
c443023
Fix captured this
lucic71 Sep 17, 2026
64c87b1
Inline function
lucic71 Sep 17, 2026
f7e0baa
Merge branch 'master' into lambdas
lucic71 Sep 17, 2026
5b226e7
Add more lambda tests
lucic71 Sep 17, 2026
f2b20e2
format
lucic71 Sep 17, 2026
eb00dec
Remove ConvertCXXRecordDecl
lucic71 Sep 17, 2026
4db5767
Push unboxed when enttering Converter::VisitCXXRecordDecl
lucic71 Sep 17, 2026
4198025
Type of declrefexpr differs based on context
lucic71 Sep 17, 2026
1204400
Update docs
lucic71 Sep 17, 2026
9004343
Merge branch 'master' into lambdas
lucic71 Sep 17, 2026
dbac4cc
Merge artifacts
lucic71 Sep 17, 2026
65b66e1
Use ensure instead of assert
lucic71 Sep 17, 2026
2b6d740
Merge branch 'master' into lambdas
lucic71 Sep 17, 2026
dbb61ac
Update tests
lucic71 Sep 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 105 additions & 90 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,21 +209,6 @@ bool Converter::VisitBuiltinType(clang::BuiltinType *type) {

bool Converter::VisitRecordType(clang::RecordType *type) {
auto *decl = type->getDecl();
if (auto lambda = clang::dyn_cast<clang::CXXRecordDecl>(decl)) {
if (lambda->isLambda()) {
if (in_function_formals_) {
StrCat(
ConvertFunctionPointerType(lambda->getLambdaCallOperator()
->getType()
->getAs<clang::FunctionProtoType>(),
FnProtoType::LambdaCallOperator));
} else {
StrCat('_');
}
return false;
}
}

StrCat(GetRecordName(decl));
Mapper::AddRuleForUserDefinedType(decl);
return false;
Expand Down Expand Up @@ -322,10 +307,8 @@ bool Converter::VisitReferenceType(clang::ReferenceType *type) {
}

std::string
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto,
FnProtoType kind) {
std::string result =
(kind == FnProtoType::LambdaCallOperator ? "impl Fn(" : "fn(");
Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto) {
std::string result = "fn(";
for (auto p_ty : proto->param_types()) {
result += ToString(p_ty);
result += ',';
Expand Down Expand Up @@ -560,20 +543,6 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
return true;
}

bool Converter::ConvertLambdaVarDecl(clang::VarDecl *decl) {
if (decl->getType()->isFunctionPointerType()) {
return false;
}
if (decl->hasInit()) {
if (clang::isa<clang::LambdaExpr>(
decl->getInit()->IgnoreUnlessSpelledInSource())) {
// Lambdas are inlined at the call site.
return true;
}
}
return false;
}

void Converter::ConvertVarDeclInitializer(clang::VarDecl *decl) {
if (decl->hasInit()) {
ConvertVarInit(decl->getType(), decl->getInit());
Expand Down Expand Up @@ -628,10 +597,6 @@ void Converter::ConvertGlobalVarDecl(clang::VarDecl *decl) {
}

bool Converter::VisitVarDecl(clang::VarDecl *decl) {
if (ConvertLambdaVarDecl(decl)) {
return false;
}

if (IsGlobalVar(decl)) {
ConvertGlobalVarDecl(decl);
} else {
Expand Down Expand Up @@ -961,7 +926,6 @@ void Converter::EmitRustUnion(clang::RecordDecl *decl) {

bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
decl->dump(log());

Mapper::AddRuleForUserDefinedType(decl);
if (!IsConvertibleCXXRecordDecl(decl)) {
return false;
Expand Down Expand Up @@ -991,6 +955,10 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {

DefineImplicitMembers(decl);
EmitRustStructOrUnion(decl);
if (decl->isLambda()) {
AddCallableTrait(decl);
AddFunctionPointerConversion(decl);
}
} else if (decl->isUnion()) {
if (!record_decls_.MarkDefined(GetRecordName(decl))) {
return false;
Expand All @@ -1000,7 +968,6 @@ bool Converter::VisitCXXRecordDecl(clang::CXXRecordDecl *decl) {
// FIXME: improve error handling
assert(0 && "unsupported record kind");
}

return false;
}

Expand Down Expand Up @@ -1090,15 +1057,15 @@ bool Converter::ConvertCXXMethodDecl(clang::CXXMethodDecl *decl) {
}

if (method_target_ == MethodTarget::ValueImpl &&
(decl->isStatic() ||
(IsStaticMethod(decl) ||
(!decl->isVirtual() && !decl->getParent()->isAbstract()))) {
ConvertFunctionQualifiers(decl);
}
StrCat(keyword_unsafe_, keyword::kFn, GetMethodName(decl));

{
PushParen paren(*this);
if (!decl->isStatic()) {
if (!IsStaticMethod(decl)) {
StrCat(GetSelfMaybeWithMut(decl), token::kComma);
}
ConvertFunctionParameters(decl);
Expand Down Expand Up @@ -2118,7 +2085,7 @@ void Converter::ConvertUserOperatorCall(clang::CXXOperatorCallExpr *expr) {
auto info = CollectCallInfo(expr);
EmitHoistedArgs(info);
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(callee)) {
if (method->isInstance()) {
if (!IsStaticMethod(method)) {
SetUFCSReceiver(expr->getArg(0), false, method);
}
StrCat(GetUFCSName(method), token::kDoubleColon, GetMethodName(method));
Expand Down Expand Up @@ -2748,7 +2715,7 @@ bool Converter::IsReferenceType(const clang::Expr *expr) const {
GetReturnTypeOfFunction(call)->isReferenceType();
}
if (const auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(e)) {
return decl_ref->getDecl()->getType()->isReferenceType();
return GetDeclRefType(curr_function_, decl_ref)->isReferenceType();
}
if (const auto *member = clang::dyn_cast<clang::MemberExpr>(e)) {
return member->getMemberDecl()->getType()->isReferenceType();
Expand Down Expand Up @@ -2911,6 +2878,10 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
}

std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {
if (auto *field = GetLambdaCapturedField(curr_function_, expr->getDecl())) {
return std::format("{}.{}", keyword::kSelfValue,
GetNamedDeclAsString(field));
}
if (isAddrOf()) {
clang::Expr *addrof_op = ToAddrOf(ctx_, expr);
if (auto str = GetMappedAsString(addrof_op); !str.empty()) {
Expand All @@ -2927,7 +2898,7 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {

if (auto *function = decl->getAsFunction()) {
if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(function)) {
if (method->isStatic()) {
if (IsStaticMethod(method)) {
return std::format("{}::{}", GetRecordName(method->getParent()),
GetNamedDeclAsString(method));
}
Expand Down Expand Up @@ -2957,10 +2928,11 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {
bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
auto str = ConvertDeclRefExpr(expr);
auto decl = expr->getDecl();
auto decl_t = GetDeclRefType(curr_function_, expr);

if (decl->getType()->getAs<clang::ReferenceType>() && !isAddrOf() &&
if (decl_t->getAs<clang::ReferenceType>() && !isAddrOf() &&
!map_iter_decls_.contains(clang::dyn_cast<clang::VarDecl>(decl))) {
EmitDeref(std::move(str), decl->getType().getNonReferenceType());
EmitDeref(std::move(str), decl_t.getNonReferenceType());
SetValueFreshness(expr->getType());
return false;
}
Expand All @@ -2975,23 +2947,8 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
return false;
}

if (auto var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
if (!var_decl->getType()->isFunctionPointerType()) {
if (auto init = var_decl->getInit()) {
if (auto lambda = clang::dyn_cast<clang::LambdaExpr>(
init->IgnoreUnlessSpelledInSource())) {
PushParen paren(*this);
VisitLambdaExpr(lambda);
computed_expr_type_ = ComputedExprType::FreshValue;
return false;
}
}
}
}

if (!decl->getType()->getAs<clang::ReferenceType>() && isAddrOf()) {
StrCat(token::kRef, decl->getType().isConstQualified() ? "" : keyword_mut_,
str);
if (!decl_t->getAs<clang::ReferenceType>() && isAddrOf()) {
StrCat(token::kRef, decl_t.isConstQualified() ? "" : keyword_mut_, str);
computed_expr_type_ = ComputedExprType::FreshPointer;
return false;
}
Expand Down Expand Up @@ -3143,7 +3100,8 @@ bool Converter::VisitMemberExpr(clang::MemberExpr *expr) {

void Converter::SetUFCSReceiver(clang::Expr *base, bool is_arrow,
const clang::CXXMethodDecl *method) {
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts())) {
if (clang::isa<clang::CXXThisExpr>(base->IgnoreParenImpCasts()) &&
!GetLambdaOf(curr_function_)) {
bool in_ctor =
curr_function_ && clang::isa<clang::CXXConstructorDecl>(curr_function_);
ufcs_receiver_ = in_ctor ? "&mut this" : keyword::kSelfValue;
Expand Down Expand Up @@ -3217,8 +3175,8 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
}

auto *base = expr->getBase();
bool base_is_this =
clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) && !ThisIsRustPtr();
bool base_is_this = clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) &&
!ThisIsRustPtr() && !GetLambdaOf(curr_function_);
PushExprKind push(*this, isLValue() ? ExprKind::LValue : ExprKind::RValue);
if (base_is_this) {
StrCat(clang::isa<clang::CXXConstructorDecl>(curr_function_)
Expand All @@ -3236,13 +3194,19 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
StrCat(GetOverloadedFunctionName(method));
} else if (!name_override.empty()) {
StrCat(token::kDot, name_override);
} else if (member->getDeclName().isIdentifier()) {
} else if (member->getDeclName().isIdentifier() ||
clang::isa<clang::CXXConversionDecl>(member)) {
StrCat(token::kDot);
StrCat(GetNamedDeclAsString(member));
}
}

bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
if (GetLambdaOf(curr_function_)) {
StrCat(keyword::kSelfValue, token::kDot, token::kLambdaThisCapture);
computed_expr_type_ = ComputedExprType::Pointer;
return false;
}
if (clang::isa<clang::CXXConstructorDecl>(curr_function_)) {
StrCat("&raw mut this");
} else {
Expand Down Expand Up @@ -3672,24 +3636,84 @@ bool Converter::VisitConstantExpr(clang::ConstantExpr *expr) {
}

bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
if (isAddrOf() && expr->capture_size() == 0) {
StrCat("Some");
}
auto *record = expr->getLambdaClass();
{
Buffer buf(*this);
VisitCXXRecordDecl(record);
hoisted_records_ += std::move(buf).str();
}
auto *init_list = new (ctx_) clang::InitListExpr(
ctx_, {},
llvm::ArrayRef(expr->capture_init_begin(), expr->capture_size()), {},
false);
init_list->setType(expr->getType());
PushParen paren(*this);
StrCat('|');
for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) {
StrCat(GetNamedDeclAsString(p), token::kColon, ToString(p->getType()),
token::kComma);
}
StrCat("| {");
EmitFunctionPreamble(expr->getLambdaClass()->getLambdaCallOperator());
PushCurrFunction push_fn(*this,
expr->getLambdaClass()->getLambdaCallOperator());
ConvertFunctionBody(curr_function_);
StrCat('}');
Convert(init_list);
return false;
}

static constexpr unsigned kMaxCallableArity = 3;

void Converter::AddCallableTrait(clang::CXXRecordDecl *decl) {
auto *op = decl->getLambdaCallOperator();
ENSURE(op->getNumParams() <= kMaxCallableArity);
if (!op->isConst()) {
return;
}
auto ret = op->getReturnType()->isVoidType() ? std::string("()")
: ToString(op->getReturnType());
StrCat(keyword::kImpl, std::format("Callable{}", op->getNumParams()));
{
PushAngle angle(*this);
for (auto *p : op->parameters()) {
StrCat(ToString(p->getType()), token::kComma);
}
StrCat(ret);
}
StrCat("for", GetRecordName(decl));
PushBrace impl_brace(*this);
StrCat(keyword::kFn, "call");
{
PushParen paren(*this);
StrCat("&self,");
for (unsigned i = 0; auto *p : op->parameters()) {
StrCat(std::format("a{}:", ++i), ToString(p->getType()), token::kComma);
}
}
StrCat(token::kArrow, ret);
PushBrace fn_brace(*this);
StrCat(keyword_unsafe_);
PushBrace unsafe_brace(*this);
StrCat(GetUFCSName(op), token::kDoubleColon, GetMethodName(op));
PushParen call_paren(*this);
if (!IsStaticMethod(op)) {
StrCat("self,");
}
for (unsigned i = 0; i < op->getNumParams(); ++i) {
StrCat(std::format("a{},", i + 1));
}
}

std::string
Converter::ConvertLambdaToFunctionPointer(const clang::CXXMethodDecl *op) {
return std::format("Some({}::{})", GetUFCSName(op), GetMethodName(op));
}

void Converter::AddFunctionPointerConversion(clang::CXXRecordDecl *decl) {
for (auto *method : decl->methods()) {
auto *conv = clang::dyn_cast<clang::CXXConversionDecl>(method);
if (!conv) {
continue;
}
StrCat(keyword::kImpl, GetRecordName(decl));
PushBrace impl_brace(*this);
StrCat("pub fn", GetMethodName(conv), "(&self)", token::kArrow,
ToString(conv->getConversionType()));
PushBrace fn_brace(*this);
StrCat(ConvertLambdaToFunctionPointer(decl->getLambdaCallOperator()));
}
}

bool Converter::VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) {
if (auto arr_ty = clang::dyn_cast<clang::ArrayType>(
expr->getType()->getCanonicalTypeInternal().getTypePtr())) {
Expand Down Expand Up @@ -4102,15 +4126,6 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
StrCat(keyword_mut_);
}
}
if (qual_type->isFunctionPointerType()) {
if (auto *lambda = clang::dyn_cast<clang::LambdaExpr>(
expr->IgnoreUnlessSpelledInSource())) {
PushExprKind push(*this, ExprKind::AddrOf);
PushInitType init_type(*this, qual_type);
VisitLambdaExpr(lambda);
return;
}
}
auto *ignore_casts = expr->IgnoreCasts();
// FIXME: this looks very complicated
if (auto *ctor = clang::dyn_cast<clang::CXXConstructExpr>(ignore_casts);
Expand Down
15 changes: 9 additions & 6 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

enum class FnProtoType { LambdaCallOperator, FnPtr };

virtual std::string
ConvertFunctionPointerType(const clang::FunctionProtoType *proto,
FnProtoType kind = FnProtoType::FnPtr);
ConvertFunctionPointerType(const clang::FunctionProtoType *proto);

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

Expand Down Expand Up @@ -115,8 +112,6 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

virtual bool ConvertLambdaVarDecl(clang::VarDecl *decl);

bool VisitRecordDecl(clang::RecordDecl *decl);

virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl);
Expand Down Expand Up @@ -432,6 +427,13 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

virtual void AddCallableTrait(clang::CXXRecordDecl *decl);

virtual void AddFunctionPointerConversion(clang::CXXRecordDecl *decl);

virtual std::string
ConvertLambdaToFunctionPointer(const clang::CXXMethodDecl *op);

virtual bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr);
virtual bool VisitCXXScalarValueInitExpr(clang::CXXScalarValueInitExpr *expr);

Expand Down Expand Up @@ -510,6 +512,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
PushDelim<token::kOpenCurlyBracket, token::kCloseCurlyBracket>;
using PushParen = PushDelim<token::kOpenParen, token::kCloseParen>;
using PushBracket = PushDelim<token::kOpenBracket, token::kCloseBracket>;
using PushAngle = PushDelim<token::kLt, token::kGt>;
using PushLazyType = PushDelim<token::kLazyCellType, token::kGt>;
using PushLazyInit = PushDelim<token::kLazyCellNew, token::kCloseParen>;

Expand Down
Loading
Loading