Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -1495,125 +1495,156 @@ 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);

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

if (is_materialize_to_ref) {
auto [binding, ref] =
MaterializeTemp(std::format("_{}", param_name), param_type, arg);
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(";");
CallArg ca{
.expr = arg,
.kind = Kind::Hoisted,
.param_name = function ? function->getParamDecl(i)->getNameAsString()
: ("arg" + std::to_string(i)),
.param_type = function ? function->getParamDecl(i)->getType()
: proto->getParamType(i),
.has_default = function && function->getParamDecl(i)->hasDefaultArg(),
};
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));
}
}

if (proto && !function) {
EmitFnPtrCall(callee);
return info;
}

void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) {
if (param_type->isLValueReferenceType()) {
PushExprKind push(*this, ExprKind::AddrOf);
ConvertVarInit(param_type, expr);
} else {
PushExprKind push(*this, ExprKind::Callee);
Convert(callee);
ConvertVarInit(param_type, expr);
}
{
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(')');
}
StrCat(token::kComma);
}

void Converter::EmitHoistedArgs(CallInfo &info) {
using Kind = CallArg::Kind;
for (auto &ca : info.args) {
switch (ca.kind) {
case Kind::Hoisted:
StrCat("let",
std::format("_{}: {}", ca.param_name, ToString(ca.param_type)),
"=");
ConvertParamTy(ca.param_type, ca.expr);
StrCat(";");
break;
case Kind::Materialized: {
auto [binding, ref] = MaterializeTemp(std::format("_{}", ca.param_name),
ca.param_type, ca.expr);
StrCat(binding);
ca.ref_temp_name = std::move(ref);
break;
}
case Kind::Inline:
break;
}
}
}

void Converter::EmitArgList(const CallInfo &info) {
using Kind = CallArg::Kind;
PushParen call_args(*this);

// 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);
ConvertVariadicArg(arg);
StrCat(".into()", token::kComma);
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(std::format("_{}", ca.param_name));
break;
case Kind::Materialized:
StrCat(ca.ref_temp_name);
break;
case Kind::Inline:
ConvertParamTy(ca.param_type, ca.expr);
break;
}
StrCat(']');
}

StrCat(token::kComma);
}

if (info.is_variadic) {
StrCat(token::kRef);
PushBracket push(*this);
for (auto *arg : info.variadic_args) {
ConvertVariadicArg(arg);
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 @@ -217,6 +217,39 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

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

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

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

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

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