Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
4aaa112
Refactor ConvertGenericCallExpr
lucic71 May 23, 2026
8422cda
EmitArgBindings -> EmitHoistedArgs
lucic71 May 23, 2026
79a0992
Drop default initialization fom CallInfo
lucic71 May 24, 2026
0e9484f
Add prefix on param_name from construction
lucic71 May 24, 2026
49ef2b1
Pass CallInfo by rvalue reference
lucic71 May 24, 2026
fc309a6
Reorder CallArg fields by size
lucic71 May 24, 2026
be78090
Reorder initialization
lucic71 May 24, 2026
a605853
Reorder CallInfo fields
lucic71 May 27, 2026
a785482
Add codegen support for goto (#163)
lucic71 Jun 3, 2026
42a07be
Translate access to flexible array member (#165)
lucic71 Jun 1, 2026
15fbe1d
Avoid collision between tag and typedef identifier (#166)
lucic71 Jun 3, 2026
a194df9
Handle non-printable ASCII in str and byte arrays (#169)
lucic71 Jun 3, 2026
fe2f472
Fix `Ord` codegen to use `other < self` in reverse branch (#170)
Copilot Jun 3, 2026
891afa8
Handle char array initialization through `{ string literal }` (#175)
lucic71 Jun 3, 2026
f41f145
Fix clash between switch arm variable and local variable (#172)
lucic71 Jun 3, 2026
cd5ba7e
Rewrite in6_addr::__in6_u::__u8_addr8 as libc::in6_addr::s6_addr (#177)
lucic71 Jun 3, 2026
301a669
Fix name clash between anon enums across TU (#171)
lucic71 Jun 3, 2026
c4580d2
simplify some string rules
nunoplopes Jun 3, 2026
db6226c
Wrap variadic arg in paren before calling .into() (#168)
lucic71 Jun 3, 2026
968f1ca
Handle array to pointer decay for libc members (#176)
lucic71 Jun 4, 2026
39c5102
remove a few string copies
nunoplopes May 27, 2026
a2c71b8
minor code simplifications
nunoplopes May 27, 2026
a6d0d48
Fix do while continue stmt (#160)
lucic71 May 31, 2026
0181b75
Convert single-character `StrCat` string args to char literals (#178)
Copilot Jun 3, 2026
7be3538
Update tests
lucic71 Jun 4, 2026
063bd73
Delete extra paren
lucic71 Jun 4, 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
203 changes: 117 additions & 86 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1594,128 +1594,159 @@ void Converter::ConvertFunctionToFunctionPointer(
StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl)));
}

void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
clang::Expr *callee = expr->getCallee();
auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) {
if (param_type->isLValueReferenceType()) {
PushExprKind push(*this, ExprKind::AddrOf);
ConvertVarInit(param_type, expr);
} else {
ConvertVarInit(param_type, expr);
}
};
Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
using Kind = CallArg::Kind;

unsigned arg_begin = 0; // skip count for operator()'s implicit object arg
CallInfo info;
info.callee = expr->getCallee();
unsigned arg_begin = 0;
if (auto op_call = llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
if (op_call->getOperator() == clang::OO_Call) {
callee = op_call->getArg(0);
info.callee = op_call->getArg(0);
arg_begin = 1;
}
}

PushParen outer(*this);
StrCat(keyword_unsafe_);
PushBrace unsafe_brace(*this);
const auto *function =
expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr;
const clang::FunctionProtoType *proto = nullptr;

if (!function) {
auto callee_ty = callee->getType().getDesugaredType(ctx_);
auto callee_ty = info.callee->getType().getDesugaredType(ctx_);
if (auto ptr_ty = callee_ty->getAs<clang::PointerType>()) {
proto = ptr_ty->getPointeeType()->getAs<clang::FunctionProtoType>();
}
}

assert((function || proto) &&
"Either function decl or function prototype should be known");

auto num_args = expr->getNumArgs() - arg_begin;
bool is_variadic =
function ? function->isVariadic() : (proto && proto->isVariadic());
unsigned num_named_params = function
? function->getNumParams()
: (proto ? proto->getNumParams() : num_args);

// Track which args are materialized temps bound to reference params
std::vector<std::string> temp_refs(num_args);
unsigned num_args = expr->getNumArgs() - arg_begin;
unsigned num_named_params =
function ? function->getNumParams() : proto->getNumParams();
info.is_variadic = function ? function->isVariadic() : proto->isVariadic();
info.is_fn_ptr_call = !function;

for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
auto *arg = expr->getArg(i + arg_begin);
std::string param_name = function
? function->getParamDecl(i)->getNameAsString()
: ("arg" + std::to_string(i));
clang::QualType param_type = function ? function->getParamDecl(i)->getType()
: proto->getParamType(i);
CallArg ca{
.param_name = function
? ("_" + function->getParamDecl(i)->getNameAsString())
: ("_arg" + std::to_string(i)),
.param_type = function ? function->getParamDecl(i)->getType()
: proto->getParamType(i),
.expr = arg,
.has_default = function && function->getParamDecl(i)->hasDefaultArg(),
.kind = Kind::Hoisted,
};
bool is_materialize = clang::isa<clang::MaterializeTemporaryExpr>(arg);
if (is_materialize && ca.param_type->isLValueReferenceType()) {
ca.kind = Kind::Materialized;
} else if (is_materialize) {
ca.kind = Kind::Inline;
}
info.args.push_back(std::move(ca));
}

if (info.is_variadic) {
for (unsigned i = num_named_params; i < num_args; ++i) {
info.variadic_args.push_back(expr->getArg(i + arg_begin));
}
}

bool is_materialize_to_ref =
clang::isa<clang::MaterializeTemporaryExpr>(arg) &&
param_type->isLValueReferenceType();
return info;
}

if (is_materialize_to_ref) {
void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) {
if (param_type->isLValueReferenceType()) {
PushExprKind push(*this, ExprKind::AddrOf);
ConvertVarInit(param_type, expr);
} else {
ConvertVarInit(param_type, expr);
}
}

void Converter::EmitHoistedArgs(CallInfo &info) {
using Kind = CallArg::Kind;
for (auto &ca : info.args) {
switch (ca.kind) {
case Kind::Hoisted:
StrCat(
std::format("let {}: {} =", ca.param_name, ToString(ca.param_type)));
ConvertParamTy(ca.param_type, ca.expr);
StrCat(";");
break;
case Kind::Materialized: {
auto [binding, ref] =
MaterializeTemp(std::format("_{}", param_name), param_type, arg);
MaterializeTemp(ca.param_name, ca.param_type, ca.expr);
StrCat(binding);
temp_refs[i] = std::move(ref);
} else if (!clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
StrCat("let", std::format("_{}: {}", param_name, ToString(param_type)),
'=');
convert_param_ty(param_type, arg);
StrCat(';');
ca.ref_temp_name = std::move(ref);
break;
}
case Kind::Inline:
break;
}
}
}

if (proto && !function) {
EmitFnPtrCall(callee);
} else {
PushExprKind push(*this, ExprKind::Callee);
Convert(callee);
}
{
PushParen call_args(*this);
for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
auto *arg = expr->getArg(i + arg_begin);
std::string param_name =
function ? function->getParamDecl(i)->getNameAsString()
: ("arg" + std::to_string(i));
clang::QualType param_type = function
? function->getParamDecl(i)->getType()
: proto->getParamType(i);
bool is_parm_with_default_value =
function && function->getParamDecl(i)->hasDefaultArg();

if (is_parm_with_default_value) {
StrCat("Some(");
}
if (!temp_refs[i].empty()) {
StrCat(temp_refs[i]);
} else if (clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
convert_param_ty(param_type, arg);
} else {
StrCat(std::format("_{}", param_name));
}
if (is_parm_with_default_value) {
StrCat(')');
void Converter::EmitArgList(const CallInfo &info) {
using Kind = CallArg::Kind;
PushParen call_args(*this);

for (const auto &ca : info.args) {
if (ca.has_default) {
StrCat("Some");
}

{
PushParen push(*this, ca.has_default);
switch (ca.kind) {
case Kind::Hoisted:
StrCat(ca.param_name);
break;
case Kind::Materialized:
StrCat(ca.ref_temp_name);
break;
case Kind::Inline:
ConvertParamTy(ca.param_type, ca.expr);
break;
}
StrCat(token::kComma);
}

// Variadic args: wrap in &[arg.into(), ...]
if (is_variadic) {
StrCat("& [");
for (unsigned i = num_named_params; i < num_args; ++i) {
auto *arg = expr->getArg(i + arg_begin);
{
PushParen p(*this);
ConvertVariadicArg(arg);
}
StrCat(".into()", token::kComma);
StrCat(token::kComma);
}

if (info.is_variadic) {
StrCat(token::kRef);
PushBracket push(*this);
for (auto *arg : info.variadic_args) {
{
PushParen p(*this);
ConvertVariadicArg(arg);
}
StrCat(']');
StrCat(".into()", token::kComma);
}
}
}

void Converter::EmitCall(CallInfo &&info) {
EmitHoistedArgs(info);

if (info.is_fn_ptr_call) {
EmitFnPtrCall(info.callee);
} else {
PushExprKind push(*this, ExprKind::Callee);
Convert(info.callee);
}

EmitArgList(info);
}

void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
PushParen outer(*this);
StrCat(keyword_unsafe_);
PushBrace unsafe_brace(*this);
EmitCall(CollectCallInfo(expr));
}

std::optional<Converter::TempMaterializationCtx>
Converter::ConvertCallExpr(clang::CallExpr *expr) {
auto *callee = expr->getCallee();
Expand Down
33 changes: 33 additions & 0 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,39 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

std::optional<TempMaterializationCtx> ConvertCallExpr(clang::CallExpr *expr);

struct CallArg {
enum class Kind : int8_t {
Hoisted,
Inline,
Materialized,
};

std::string param_name;
std::string ref_temp_name;
clang::QualType param_type;
clang::Expr *expr;
bool has_default;
Kind kind;
};

struct CallInfo {
std::vector<CallArg> args;
std::vector<clang::Expr *> variadic_args;
clang::Expr *callee;
bool is_variadic;
bool is_fn_ptr_call;
};

CallInfo CollectCallInfo(clang::CallExpr *expr);

void ConvertParamTy(clang::QualType param_type, clang::Expr *expr);

void EmitHoistedArgs(CallInfo &info);

void EmitArgList(const CallInfo &info);

void EmitCall(CallInfo &&info);

void ConvertGenericCallExpr(clang::CallExpr *expr);

virtual void EmitFnPtrCall(clang::Expr *callee);
Expand Down
Loading