Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
117afb3
Translate free function ptr as Option<fn>
lucic71 Apr 13, 2026
562978b
Fix null ptr deref
lucic71 Apr 13, 2026
32ae03c
Fix translation of lambdas
lucic71 Apr 13, 2026
c431b29
Translate type of lambda as _ instead of Rc<dyn Fn>
lucic71 Apr 13, 2026
de96315
Translate non-const globals as static mut
lucic71 Apr 13, 2026
212d22b
Use concerete default val for initializing globals
lucic71 Apr 13, 2026
58c39b9
Help the compiler deduce the type of a function
lucic71 Apr 13, 2026
12a4433
Don't add &mut in function ptr conditionals
lucic71 Apr 13, 2026
7ab067d
Add fn_ptr and lambda tests
lucic71 Apr 13, 2026
ddd13a8
clang-format
lucic71 Apr 13, 2026
4d93cd4
Update tests
lucic71 Apr 13, 2026
691a435
clang-format
lucic71 Apr 13, 2026
f026728
Translate fn ptr cast using mem::transmute
lucic71 Apr 13, 2026
e320bdb
Add support for function pointers in PtrKind
lucic71 Apr 14, 2026
a5d65f6
Move PtrKind::Fn into FnPtr
lucic71 Apr 14, 2026
01e9e9f
Fix signature of fread and fwrite
lucic71 Apr 15, 2026
e3a69af
Add name and module in ExprTgt
lucic71 Apr 15, 2026
e30fef0
Use rule function name in AddrOf context
lucic71 Apr 15, 2026
133e94f
Check name and module are never empty
lucic71 Apr 15, 2026
25b203f
Get fully qualified name of translation rule
lucic71 Apr 15, 2026
9cfda12
Update output of fn_ptr_stdlib_compare
lucic71 Apr 15, 2026
06b73fd
Change to deafult with suffix
lucic71 Apr 15, 2026
942bde3
Make rules public
lucic71 Apr 15, 2026
03e7ab7
cargo-fmt
lucic71 Apr 15, 2026
a2e1c1a
Delete unused Cargo.toml
lucic71 Apr 15, 2026
1c1ab7d
Use prebuilt rules and libcc2rs targets
lucic71 Apr 15, 2026
840863d
Implement Deref for FnPtr
lucic71 Apr 15, 2026
3c1b9c7
Update rules IR
lucic71 Apr 15, 2026
e7fa221
Remove unused fn ptr cast and fn_ptr_anon
lucic71 Apr 15, 2026
0c1ebf9
Delete ByteRepr for fn pointers
lucic71 Apr 15, 2026
60fd8c3
Drop Clone trait from ErasedPtr
lucic71 Apr 15, 2026
81581df
Allow casting between nullptr's of different types
lucic71 Apr 15, 2026
82b9ed0
Update output of fn_ptr_stdlib_compare
lucic71 Apr 15, 2026
207106c
Allow char*/void* <-> T* conversions in adapter
lucic71 Apr 15, 2026
c500cff
Omit enumeral types on generating default integers
lucic71 Apr 15, 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
183 changes: 121 additions & 62 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,20 @@ bool Converter::VisitRecordType(clang::RecordType *type) {
auto *decl = type->getDecl();
if (auto lambda = clang::dyn_cast<clang::CXXRecordDecl>(decl)) {
if (lambda->isLambda()) {
auto call_op = lambda->getLambdaCallOperator();
StrCat("Rc<dyn Fn(");
for (auto p : call_op->parameters()) {
StrCat(std::format("{},", ToStringBase(p->getType())));
}
StrCat(")");
if (!call_op->getReturnType()->isVoidType()) {
StrCat("->");
StrCat(ToStringBase(call_op->getReturnType()));
if (in_function_formals_) {
auto call_op = lambda->getLambdaCallOperator();
StrCat("impl Fn(");
for (auto p : call_op->parameters()) {
StrCat(std::format("{},", ToStringBase(p->getType())));
}
StrCat(")");
if (!call_op->getReturnType()->isVoidType()) {
StrCat("->");
StrCat(ToStringBase(call_op->getReturnType()));
}
} else {
StrCat("_");
}
StrCat(">");
return false;
}
}
Expand Down Expand Up @@ -226,7 +229,7 @@ void Converter::ConvertFunctionPointerType(clang::PointerType *type) {
auto proto = type->getPointeeType()->getAs<clang::FunctionProtoType>();
assert(proto && "Type should be a function prototype");

StrCat("Rc<dyn Fn(");
StrCat("Option<", keyword_unsafe_, " fn(");
for (auto p_ty : proto->param_types()) {
StrCat(std::format("{},", ToString(p_ty)));
}
Expand Down Expand Up @@ -383,9 +386,15 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
return false;
}
StrCat(AccessSpecifierAsString(decl->getAccess()), keyword::kStatic);
if (!qual_type.isConstQualified()) {
StrCat(keyword_mut_);
}
ENSURE(decl_ids_.insert(GetID(decl)).second);
} else if (decl->isStaticLocal()) {
StrCat(keyword::kStatic);
if (!qual_type.isConstQualified()) {
StrCat(keyword_mut_);
}
} else if (decl->isLocalVarDecl()) {
StrCat(keyword::kLet);
}
Expand Down Expand Up @@ -417,6 +426,9 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
}

bool Converter::ConvertLambdaVarDecl(clang::VarDecl *decl) {
if (decl->getType()->isFunctionPointerType()) {
return false;
}
if (decl->hasInit()) {
if (clang::isa<clang::LambdaExpr>(
decl->getInit()->IgnoreUnlessSpelledInSource())) {
Expand Down Expand Up @@ -1362,6 +1374,31 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
return false;
}

void Converter::EmitFnPtrCall(clang::Expr *callee) {
StrCat(token::kOpenParen);
Convert(callee);
StrCat(").unwrap()");
}

void Converter::EmitFnAsValue(const clang::FunctionDecl *fn_decl) {
StrCat(std::format("Some({})", Mapper::GetFnRefName(fn_decl)));
}

std::string Converter::GetPointeeRustType(clang::QualType ptr_type) {
auto pointee = ptr_type->getPointeeType();
if (pointee->isIntegerType()) {
return ToString(pointee);
}
auto str = ToString(ptr_type);
while (!str.empty() && std::isspace(str.back())) {
str.pop_back();
}
if (str.starts_with("Ptr<") && str.ends_with(">")) {
return str.substr(4, str.size() - 5);
}
return ToString(pointee);
}

void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
clang::Expr *callee = expr->getCallee();
auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) {
Expand All @@ -1384,7 +1421,8 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
StrCat(token::kOpenParen);
StrCat(keyword_unsafe_);
StrCat(token::kOpenCurlyBracket);
const auto *function = expr->getCalleeDecl()->getAsFunction();
const auto *function =
expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr;
const clang::FunctionProtoType *proto = nullptr;

if (!function) {
Expand Down Expand Up @@ -1432,7 +1470,12 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
}
}

Convert(callee);
if (proto && !function) {
EmitFnPtrCall(callee);
} else {
PushExprKind push(*this, ExprKind::RValue);
Convert(StripFunctionPointerDecay(callee));
}
StrCat(token::kOpenParen);
for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
auto *arg = expr->getArg(i + arg_begin);
Expand Down Expand Up @@ -1648,7 +1691,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
break;
}
case clang::CastKind::CK_FunctionToPointerDecay:
case clang::CastKind::CK_BuiltinFnToFnPtr:
case clang::CastKind::CK_BuiltinFnToFnPtr: {
PushExprKind push(*this, ExprKind::AddrOf);
Convert(sub_expr);
break;
}
case clang::CastKind::CK_ConstructorConversion:
case clang::CastKind::CK_DerivedToBase:
Convert(sub_expr);
Expand All @@ -1663,7 +1710,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
ConvertEqualsNullPtr(sub_expr);
break;
case clang::CastKind::CK_NullToPointer:
StrCat(keyword_default_);
if (type->isFunctionPointerType()) {
StrCat("None");
} else {
StrCat(keyword_default_);
}
computed_expr_type_ = ComputedExprType::FreshPointer;
break;
default:
Expand Down Expand Up @@ -1708,6 +1759,17 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) {
if (expr->getType() == sub_expr->getType()) {
return Convert(sub_expr);
}
if (type->isFunctionPointerType() ||
sub_expr->getType()->isFunctionPointerType()) {
StrCat("std::mem::transmute::<");
Convert(sub_expr->getType());
StrCat(",");
Convert(type);
StrCat(">(");
Convert(sub_expr);
StrCat(")");
return false;
}
StrCat(token::kOpenParen);
Convert(sub_expr);
if (auto *unary_oper = clang::dyn_cast<clang::UnaryOperator>(sub_expr);
Expand Down Expand Up @@ -1934,12 +1996,12 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
StrCat(keyword::kIf);
Convert(expr->getCond());
StrCat(token::kOpenCurlyBracket);
if (expr->isLValue() && !isRValue()) {
if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) {
StrCat(token::kRef, keyword_mut_);
}
Convert(expr->getTrueExpr());
StrCat(token::kCloseCurlyBracket, keyword::kElse, token::kOpenCurlyBracket);
if (expr->isLValue() && !isRValue()) {
if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) {
StrCat(token::kRef, keyword_mut_);
}
Convert(expr->getFalseExpr());
Expand All @@ -1956,7 +2018,8 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) {
}

auto *decl = expr->getDecl();
if (Mapper::Contains(expr)) {
if (!(clang::isa<clang::FunctionDecl>(decl) && isAddrOf()) &&
Mapper::Contains(expr)) {
return GetMappedAsString(expr);
} else if (auto *function = decl->getAsFunction()) {
if (auto method = clang::dyn_cast<clang::CXXMethodDecl>(function)) {
Expand Down Expand Up @@ -1993,35 +2056,23 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
return false;
}

if (auto function = clang::dyn_cast<clang::FunctionDecl>(decl)) {
if (auto *fn_decl = clang::dyn_cast<clang::FunctionDecl>(decl)) {
if (isAddrOf()) {
// Wrap unsafe function in safe closure because the Fn trait only accepts
// safe functions
std::string arguments;
for (unsigned i = 0; i < function->getNumParams(); i++) {
arguments += (i ? ", a" : "a") + std::to_string(i);
}
StrCat("Rc::new", token::kOpenParen);
StrCat(std::format("|{}|", arguments));
StrCat(keyword_unsafe_, token::kOpenCurlyBracket);
StrCat(str);
StrCat(token::kOpenParen);
StrCat(arguments);
StrCat(token::kCloseParen);
StrCat(token::kCloseCurlyBracket);
StrCat(token::kCloseParen);
EmitFnAsValue(fn_decl);
return false;
}
}

if (auto var_decl = clang::dyn_cast<clang::VarDecl>(decl)) {
if (auto init = var_decl->getInit()) {
if (auto lambda = clang::dyn_cast<clang::LambdaExpr>(
init->IgnoreUnlessSpelledInSource())) {
StrCat(token::kOpenParen);
VisitLambdaExpr(lambda);
StrCat(token::kCloseParen);
return false;
if (!var_decl->getType()->isFunctionPointerType()) {
if (auto init = var_decl->getInit()) {
if (auto lambda = clang::dyn_cast<clang::LambdaExpr>(
init->IgnoreUnlessSpelledInSource())) {
StrCat(token::kOpenParen);
VisitLambdaExpr(lambda);
StrCat(token::kCloseParen);
return false;
}
}
}
}
Expand Down Expand Up @@ -2480,6 +2531,9 @@ bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) {
}

bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) {
if (isAddrOf() && expr->capture_size() == 0) {
StrCat("Some");
}
StrCat(token::kOpenParen);
StrCat("|");
for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) {
Expand Down Expand Up @@ -2609,15 +2663,7 @@ bool Converter::VisitCXXStdInitializerListExpr(

std::string
Converter::GetFunctionPointerDefaultAsString(clang::QualType qual_type) {
std::string ret;
auto proto = qual_type->getPointeeType()->getAs<clang::FunctionProtoType>();
assert(proto);
ret = "Rc::new(|";
for (unsigned i = 0; i < proto->getNumParams(); i++) {
ret += "_,";
}
ret += R"(| { panic!("ub: uninit function pointer") }))";
return ret;
return "None";
}

std::string Converter::GetDefaultAsString(clang::QualType qual_type) {
Expand Down Expand Up @@ -2679,18 +2725,18 @@ std::string Converter::GetDefaultAsString(clang::QualType qual_type) {
}

std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) {
static llvm::DenseMap<unsigned, std::string> default_for_type = {
{clang::BuiltinType::Char_U, "0_u8"},
{clang::BuiltinType::SChar, "0_i8"},
{clang::BuiltinType::UChar, "0_u8"},
};

qual_type = qual_type.getUnqualifiedType().getCanonicalType();
if (auto builtin = qual_type->getAs<clang::BuiltinType>()) {
auto it = default_for_type.find(builtin->getKind());
if (it != default_for_type.end()) {
return it->second;
}

if (qual_type->isBooleanType()) {
return "false";
}

if (qual_type->isIntegerType() && !qual_type->isEnumeralType()) {
return std::format("0_{}", ToString(qual_type));
}

if (qual_type->isFloatingType()) {
return std::format("0.0_{}", ToString(qual_type));
}

return std::format("<{}>::default()", ToString(qual_type));
Expand Down Expand Up @@ -2771,6 +2817,16 @@ 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);
curr_init_type_.push(qual_type);
VisitLambdaExpr(lambda);
curr_init_type_.pop();
return;
}
}
auto *ignore_casts = expr->IgnoreCasts();
// FIXME: this looks very complicated
if (auto *ctor = clang::dyn_cast<clang::CXXConstructExpr>(ignore_casts);
Expand Down Expand Up @@ -2816,7 +2872,8 @@ void Converter::ConvertUnsignedArithOperand(clang::Expr *expr,
void Converter::ConvertEqualsNullPtr(clang::Expr *expr) {
StrCat("(");
Convert(expr);
if (IsUniquePtr(expr->getType())) {
if (IsUniquePtr(expr->getType()) ||
expr->getType()->isFunctionPointerType()) {
StrCat(").is_none()");
} else {
StrCat(").is_null()");
Expand Down Expand Up @@ -3201,6 +3258,8 @@ void Converter::PlaceholderCtx::dump() const {

std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
const PlaceholderCtx &ph_ctx) {
arg = StripFunctionPointerDecay(arg);

if (ph_ctx.needs_materialization()) {
auto materialized = ph_ctx.materialize_ctx->GetOrMaterialize(
static_cast<unsigned>(ph_ctx.materialize_idx),
Expand Down
9 changes: 8 additions & 1 deletion cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,12 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

void ConvertGenericCallExpr(clang::CallExpr *expr);

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

virtual void EmitFnAsValue(const clang::FunctionDecl *fn_decl);

std::string GetPointeeRustType(clang::QualType ptr_type);

virtual void ConvertPrintf(clang::CallExpr *expr);

void ConvertVAArgCall(clang::CallExpr *expr);
Expand Down Expand Up @@ -332,7 +338,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
virtual bool Convert(clang::Stmt *stmt);
virtual bool Convert(clang::Expr *expr);

std::string GetFunctionPointerDefaultAsString(clang::QualType qual_type);
virtual std::string
GetFunctionPointerDefaultAsString(clang::QualType qual_type);

virtual std::string GetDefaultAsString(clang::QualType qual_type);

Expand Down
31 changes: 21 additions & 10 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,24 +320,35 @@ const char *AccessSpecifierAsString(clang::AccessSpecifier spec) {
}

clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr) {
auto decl = expr->getCalleeDecl();
if (decl->getAsFunction()) {
return decl->getAsFunction()->getReturnType().getCanonicalType();
if (auto *decl = expr->getCalleeDecl()) {
if (auto *fn = decl->getAsFunction()) {
return fn->getReturnType().getCanonicalType();
}
}

auto callee_ty =
expr->getCallee()->getType().getDesugaredType(decl->getASTContext());
if (auto ptr_ty = callee_ty->getAs<clang::PointerType>()) {
return ptr_ty->getPointeeType()
->getAs<clang::FunctionProtoType>()
->getReturnType()
.getCanonicalType();
auto callee_ty = expr->getCallee()->getType();
if (auto *ptr_ty = callee_ty->getAs<clang::PointerType>()) {
if (auto *fn_ty =
ptr_ty->getPointeeType()->getAs<clang::FunctionProtoType>()) {
return fn_ty->getReturnType().getCanonicalType();
}
}

assert(0 && "Unhandled function prototype");
return {};
}

clang::Expr *StripFunctionPointerDecay(clang::Expr *expr) {
if (auto *ice = clang::dyn_cast<clang::ImplicitCastExpr>(expr)) {
auto ck = ice->getCastKind();
if (ck == clang::CK_FunctionToPointerDecay ||
ck == clang::CK_BuiltinFnToFnPtr) {
return ice->getSubExpr();
}
}
return expr;
}

std::string GetOverloadedOperator(const clang::FunctionDecl *decl) {
switch (decl->getOverloadedOperator()) {
case clang::OO_Less:
Expand Down
Loading